Merge branch 'main' of ssh://gitlab.fdmci.hva.nl/propedeuse-hbo-ict/onderwijs/2023-2024/out-a-se-ti/blok-4/muupooviixee66

This commit is contained in:
SebasKoedam
2024-05-22 11:45:22 +02:00
13 changed files with 260 additions and 158 deletions

View File

@@ -31,6 +31,7 @@
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_sport_menu.xml" value="0.1" /> <entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/activity_sport_menu.xml" value="0.1" />
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/header.xml" value="0.125" /> <entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/header.xml" value="0.125" />
<entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/toolbar.xml" value="0.125" /> <entry key="..\:/Users/sebas/Documents/HvA/Reposetories/muupooviixee66/code/src/Fitbot/app/src/main/res/layout/toolbar.xml" value="0.125" />
<entry key="app/src/main/res/layout/activity_end_screen.xml" value="0.1" />
<entry key="app/src/main/res/layout/activity_fitness.xml" value="0.23550724637681159" /> <entry key="app/src/main/res/layout/activity_fitness.xml" value="0.23550724637681159" />
<entry key="app/src/main/res/layout/activity_main.xml" value="0.1" /> <entry key="app/src/main/res/layout/activity_main.xml" value="0.1" />
<entry key="app/src/main/res/layout/activity_sport_item.xml" value="0.2341485507246377" /> <entry key="app/src/main/res/layout/activity_sport_item.xml" value="0.2341485507246377" />

View File

@@ -8,14 +8,17 @@ import com.example.fitbot.util.server.WebSocket;
import java.util.Objects; import java.util.Objects;
public abstract class AbstractExercise implements IWebSocketHandler { public class Exercise implements IWebSocketHandler {
private EMuscleGroup muscleGroup; private EMuscleGroup muscleGroup;
private GesturePath path; private GesturePath path;
private String title;
private String description;
private float segmentsPerSecond;
// Static fields. // Static fields.
private static WebSocket webSocket; private static WebSocket webSocket;
private static AbstractExercise currentExercise = null; private static Exercise currentExercise = null;
/** /**
@@ -23,10 +26,17 @@ public abstract class AbstractExercise implements IWebSocketHandler {
* *
* @param muscleGroup The muscle group of the exercise. * @param muscleGroup The muscle group of the exercise.
* @param path The path of the exercise. * @param path The path of the exercise.
* @param title The title of the exercise.
* @param description The description of the exercise.
* @param segmentsPerSecond The number of segments per second.
* This determines how fast the exercise should be performed.
*/ */
public AbstractExercise(EMuscleGroup muscleGroup, GesturePath path) { public Exercise(EMuscleGroup muscleGroup, String title, String description, GesturePath path, float segmentsPerSecond) {
this.muscleGroup = muscleGroup; this.muscleGroup = muscleGroup;
this.title = title;
this.description = description;
this.path = path; this.path = path;
this.segmentsPerSecond = segmentsPerSecond;
} }
/** /**
@@ -101,4 +111,19 @@ public abstract class AbstractExercise implements IWebSocketHandler {
public GesturePath getPath() { public GesturePath getPath() {
return path; return path;
} }
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
/**
* Get the speed of the exercise.
*/
public double getSegmentsPerSecond() {
return segmentsPerSecond;
}
} }

View File

@@ -0,0 +1,41 @@
package com.example.fitbot.exercise;
import com.example.fitbot.util.processing.IMotionDataConsumer;
import com.example.fitbot.util.processing.MotionData;
import com.example.fitbot.util.processing.MotionProcessor;
import com.example.fitbot.util.server.IWebSocketHandler;
import com.example.fitbot.util.server.WebSocket;
import org.joml.Vector3f;
import java.net.Socket;
public class ExerciseBuilder implements IWebSocketHandler, IMotionDataConsumer {
private MotionProcessor processor;
public ExerciseBuilder() {
this.processor = new MotionProcessor();
this.processor.setMotionDataEventHandler(this);
}
@Override
public void onDisconnected(Socket socket) {
IWebSocketHandler.super.onDisconnected(socket);
}
@Override
public void onMessageReceived(WebSocket.Message message, WebSocket.MessageReply replier) {
IWebSocketHandler.super.onMessageReceived(message, replier);
}
@Override
public void onError(Socket socket, String error) {
IWebSocketHandler.super.onError(socket, error);
}
@Override
public void accept(Vector3f transformedVector, MotionData motionData, int sampleIndex, double sampleRate, int sensorId) {
}
}

View File

@@ -50,7 +50,7 @@ public class FitnessManager {
* @param uniqueIdentifier The unique identifier of the exercise * @param uniqueIdentifier The unique identifier of the exercise
* @return The exercise, if it exists on the server. Otherwise null. * @return The exercise, if it exists on the server. Otherwise null.
*/ */
public static <T extends AbstractExercise> AbstractExercise acquireExercise(String uniqueIdentifier, Class<T> referenceClass) { public static <T extends Exercise> Exercise acquireExercise(String uniqueIdentifier, Class<T> referenceClass) {
String response = sendHTTP( String response = sendHTTP(
HOST_ADDRESS + "/acquire", "GET", "application/json", "{\"kind\":\"" + uniqueIdentifier + "\"}" HOST_ADDRESS + "/acquire", "GET", "application/json", "{\"kind\":\"" + uniqueIdentifier + "\"}"
); );

View File

@@ -1,14 +1,10 @@
package com.example.fitbot.ui.activities; package com.example.fitbot.ui.activities;
import static com.example.fitbot.sports.Animations.Animate;
import android.content.Context; import android.content.Context;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.MediaController;
import android.widget.VideoView;
import com.aldebaran.qi.sdk.QiContext; import com.aldebaran.qi.sdk.QiContext;
import com.aldebaran.qi.sdk.QiSDK; import com.aldebaran.qi.sdk.QiSDK;
@@ -17,34 +13,52 @@ import com.aldebaran.qi.sdk.design.activity.RobotActivity;
import com.example.fitbot.ui.activities.EndScreenActivity; import com.example.fitbot.ui.activities.EndScreenActivity;
import com.example.fitbot.R; import com.example.fitbot.R;
import com.example.fitbot.sports.Animations; import com.example.fitbot.sports.Animations;
import com.example.fitbot.R;
import com.example.fitbot.exercise.Exercise;
import com.example.fitbot.exercise.EMuscleGroup;
import com.example.fitbot.ui.components.PersonalMotionPreviewElement;
import com.example.fitbot.util.path.GesturePath;
import org.joml.Vector3f;
public class FitnessActivity extends RobotActivity implements RobotLifecycleCallbacks { public class FitnessActivity extends RobotActivity implements RobotLifecycleCallbacks {
PersonalMotionPreviewElement personalMotionPreviewElement;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fitness); setContentView(R.layout.activity_fitness);
VideoView videoView = findViewById(R.id.videoView); GesturePath.Builder gesturePathBuilder = new GesturePath.Builder();
videoView.setVideoURI(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.bicepvideo));
MediaController mediaController = new MediaController(this); gesturePathBuilder.addVector(new Vector3f(-.5f, -.5f, -.5f));
videoView.setMediaController(mediaController); gesturePathBuilder.addVector(new Vector3f(.5f, -.5f, -.5f));
videoView.start(); gesturePathBuilder.addVector(new Vector3f(.5f, -.5f, .5f));
gesturePathBuilder.addVector(new Vector3f(-.5f, -.5f, .5f));
gesturePathBuilder.addVector(new Vector3f(-.5f, -.5f, -.5f));
com.example.fitbot.util.ButtonNavigation.setupButtonNavigation(this, R.id.homeButton, MainActivity.class); gesturePathBuilder.addVector(new Vector3f(-.5f, .5f, -.5f));
com.example.fitbot.util.ButtonNavigation.setupButtonNavigation(this, R.id.buttonComplete, EndScreenActivity.class); gesturePathBuilder.addVector(new Vector3f(.5f, .5f, -.5f));
gesturePathBuilder.addVector(new Vector3f(.5f, .5f, .5f));
gesturePathBuilder.addVector(new Vector3f(-.5f, .5f, .5f));
gesturePathBuilder.addVector(new Vector3f(-.5f, .5f, -.5f));
personalMotionPreviewElement = findViewById(R.id.personalMotionPreviewElement);
personalMotionPreviewElement.post(() -> {
Log.i("FitnessActivity", "PersonalMotionPreviewElement.post()");
Exercise exercise = new Exercise(EMuscleGroup.ARMS, "Bicep Curls", "Oefening voor de biceps.", gesturePathBuilder.build(), 1);
personalMotionPreviewElement.initialize(exercise);
});
} }
// QiSDK.register(this, this);
//
//
@Override @Override
public void onRobotFocusGained(QiContext qiContext) { public void onRobotFocusGained(QiContext qiContext) {
// Implement your logic when the robot focus is gained // Implement your logic when the robot focus is gained
Animate("bicepcurl", qiContext); Animations.Animate("bicepcurl", qiContext);
} }
@@ -64,12 +78,4 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
super.onDestroy(); super.onDestroy();
} }
public static class PersonalMotionPreviewElement extends View { }
public PersonalMotionPreviewElement(Context context, AttributeSet attrs) {
super(context, attrs);
// Initialize your custom view here (optional)
}
}
}

View File

@@ -4,9 +4,11 @@ import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import com.example.fitbot.exercise.Exercise;
import com.example.fitbot.util.path.GesturePath; import com.example.fitbot.util.path.GesturePath;
import com.example.fitbot.util.path.PathSegment; import com.example.fitbot.util.path.PathSegment;
import com.example.fitbot.util.processing.MotionData; import com.example.fitbot.util.processing.MotionData;
@@ -22,50 +24,116 @@ public class PersonalMotionPreviewElement extends View {
private GesturePath path; private GesturePath path;
private double pathTime = 0.0D; // The timestamp at which the path is currently at. private double pathTime = 0.0D; // The timestamp at which the path is currently at.
private MotionProcessor motionProcessor; private MotionProcessor motionProcessor;
private Path referencePath, performingPath;
private Paint referencePaint, performingPaint; private Exercise exercise;
private Path referencePath; // The path the user is supposed to follow.
private Path performingPath; // The path the user is currently following.
private Path stickmanPath; // The path of the stickman that is drawn on the screen.
private Paint referencePaint;
private Paint performingPaint;
private Paint textPaint;
// Matrices for the projection of the path segments onto the screen.
// Depth buffering sadly is not supported yet due to brain dysfunction
private Matrix4f modelViewMatrix = new Matrix4f();
private Matrix4f projectionMatrix = new Matrix4f();
private double timePassed = 0.0D; // The time that has passed since the start of the exercise, in seconds.
private long startingTime = 0L;
private Paint backgroundColor = new Paint(); private Paint backgroundColor = new Paint();
/** /**
* Constants for the preview path projection. * Constants for the preview path projection.
*/ */
private final float FOV = 70.0f; // The field of view of the preview path private final float FOV = 80.0f; // The field of view of the preview path
private final float Z_NEAR = 0.1f; // The near clipping plane private final float Z_NEAR = 0.1f; // The near clipping plane
private final float Z_FAR = 1000.0f; // The far clipping plane private final float Z_FAR = 1000.0f; // The far clipping plane
private Vector3f cameraPosition = new Vector3f(0.0f, 0.0f, 0.0f); // The position of the camera private Vector3f objectPosition = new Vector3f(0.0f, 0.0f, 0.0f); // The position of the camera
private Vector2f screenDimensions = new Vector2f(); // Width and height dimensions of the screen private Vector2f screenDimensions = new Vector2f(); // Width and height dimensions of the screen
private Vector2f rotation = new Vector2f(); // Rotation vector (yaw, pitch) private Vector2f rotation = new Vector2f(); // Rotation vector (yaw, pitch)
/** public PersonalMotionPreviewElement(Context context, AttributeSet attrs) {
* Constructor for the PersonalMotionPreviewElement class. super(context, attrs);
*
* @param context The context in which this element is created.
* @param path The gesture path that will be drawn on the canvas.
*/
public PersonalMotionPreviewElement(Context context, GesturePath path) {
super(context);
Log.i("PersonalMotionPreviewElement", "Creating new PersonalMotionPreviewElement.");
this.backgroundColor = new Paint();
this.backgroundColor.setColor(0xFF000000); // Black
this.path = path;
this.motionProcessor = new MotionProcessor();
this.motionProcessor.startListening();
this.motionProcessor.setMotionDataEventHandler((processed, preprocessed, sampleIndex, sampleRate) -> {
// TODO: Implement the calculation of the `performingPath` based on the motion data
});
this.referencePath = getDrawablePath(path.getSegments());
this.performingPath = new Path();
this.referencePaint = new Paint(); this.referencePaint = new Paint();
this.referencePaint.setColor(-1); // White this.referencePaint.setColor(0xFFFF0000); // Red
this.referencePaint.setStyle(Paint.Style.STROKE); this.referencePaint.setStyle(Paint.Style.STROKE);
this.referencePaint.setStrokeWidth(5.0f); this.referencePaint.setStrokeWidth(5.0f);
this.performingPaint = new Paint(); this.performingPaint = new Paint();
this.performingPaint.setColor(0xFF0000FF); // Blue this.performingPaint.setColor(0xFF0000FF); // Blue
this.performingPaint.setStyle(Paint.Style.STROKE); this.performingPaint.setStyle(Paint.Style.STROKE);
this.performingPaint.setStrokeWidth(5.0f); this.performingPaint.setStrokeWidth(5.0f);
this.textPaint = new Paint();
this.textPaint.setColor(-1);
this.textPaint.setStyle(Paint.Style.FILL);
this.textPaint.setTextSize(50.0f);
}
/**
* Method for updating the stickman gestures.
*
* This method will update the stickman gestures based on the current
* motion data that is being processed.
*/
private void updateStickmanGestures() {
// Reset previous path
stickmanPath.reset();
// TODO: Define all arm segments:
// - Upper left and right arm
// - Lower left and right arm
// - Upper left and right leg
// - Lower left and right leg
// Update all segments based on the perceived motion data.
PathSegment upperLeftArm = new PathSegment(
new Vector3f(),
new Vector3f()
);
PathSegment[] bodySegments = new PathSegment[] {
new PathSegment(new Vector3f(0.0f, -.5f, -.5f), new Vector3f(0, 0, 0)), // Left leg
new PathSegment(new Vector3f(0.0f, -.5f, .5f), new Vector3f(0, 0, 0)), // Right leg
new PathSegment(new Vector3f(0.0f, .5f, 0.0f), new Vector3f(0, 0, 0)), // Body
new PathSegment(new Vector3f(-.25f, .25f, 0f), new Vector3f(0, 0, 0)), // Left arm
new PathSegment(new Vector3f(.25f, .25f, 0f), new Vector3f(0, 0, 0)) // Right arm
};
// TODO: Generate new path for stickman
}
/**
* Method for initializing the PersonalMotionPreviewElement.
* This method has to be called with a "post" function when the element has been
* created, otherwise the dimensions of the element aren't initialized yet, which
* will cause the vertex projections to fail (0 width and height).
*
* @param exercise The exercise that the user is currently performing.
*/
public void initialize(Exercise exercise) {
Log.i("PersonalMotionPreviewElement", "Creating new PersonalMotionPreviewElement.");
this.backgroundColor = new Paint();
this.backgroundColor.setColor(0xFF000000); // Black
this.screenDimensions.x = this.getWidth();
this.screenDimensions.y = this.getHeight();
this.performingPath = new Path();
this.referencePath = new Path();
this.startingTime = System.nanoTime(); // Set the last time to the current time
this.exercise = exercise;
this.path = exercise.getPath();
this.motionProcessor = new MotionProcessor();
this.motionProcessor.startListening();
this.motionProcessor.setMotionDataEventHandler((processed, preprocessed, sampleIndex, sampleRate, deviceId) -> {
// TODO: Implement the calculation of the `performingPath` based on the motion data
});
} }
/** /**
@@ -90,7 +158,7 @@ public class PersonalMotionPreviewElement extends View {
/** /**
* Method for setting the rotation of the preview path. * Method for setting the rotation of the preview path.
* *
* @param yaw The yaw rotation of the preview path. * @param yaw The yaw rotation of the preview path.
* @param pitch The pitch rotation of the preview path. * @param pitch The pitch rotation of the preview path.
*/ */
public void setRotation(float yaw, float pitch) { public void setRotation(float yaw, float pitch) {
@@ -98,38 +166,38 @@ public class PersonalMotionPreviewElement extends View {
} }
/** /**
* Method for projecting a 3D point onto the screen. * Method for projecting a 3D point onto the screen.
* This method converts the 3D point to 2D space using a Model-View-Projection matrix transformation. * This method converts the 3D point to 2D space using a Model-View-Projection matrix transformation.
* *
* @param point The point to cast to the screen. * @param point The point to cast to the screen.
* @param virtualWidth The width of the virtual screen. * @param virtualWidth The width of the virtual screen.
* This is used to normalize the screen coordinates. * This is used to normalize the screen coordinates.
* @param virtualHeight The height of the virtual screen. * @param virtualHeight The height of the virtual screen.
* @return The transformed vector in screen coordinates ranging from (0, 0) to (virtualWidth, virtualHeight). * @return The transformed vector in screen coordinates ranging from (0, 0) to (virtualWidth, virtualHeight).
*/ */
private Vector2f projectVertex(Vector3f point, int virtualWidth, int virtualHeight) { private Vector2f projectVertex(Vector3f point, int virtualWidth, int virtualHeight) {
Matrix4f modelViewMatrix = new Matrix4f() modelViewMatrix
.rotateX((float) Math.toRadians(rotation.x)) .identity()
.rotateY((float) Math.toRadians(rotation.y)) .translate(-objectPosition.x, -objectPosition.y, -objectPosition.z)
.translate(cameraPosition); .rotateX((float) Math.toRadians(rotation.y))
.rotateY((float) Math.toRadians(rotation.x));
Matrix4f projectionMatrix = new Matrix4f() // Transform the projection matrix to a perspective projection matrix
// Perspective transformation conserves the depth of the object
projectionMatrix
.identity()
.perspective((float) Math.toRadians(FOV), (float) virtualWidth / virtualHeight, Z_NEAR, Z_FAR); .perspective((float) Math.toRadians(FOV), (float) virtualWidth / virtualHeight, Z_NEAR, Z_FAR);
// Calculate Model-View-Projection matrix // Convert world coordinates to screen-space using MVP matrix
Matrix4f MVP = new Matrix4f()
.set(projectionMatrix)
.mul(modelViewMatrix);
// Convert to screen coordinates
Vector4f screenCoordinates = new Vector4f(point, 1.0f) Vector4f screenCoordinates = new Vector4f(point, 1.0f)
.mul(MVP); .mul(this.modelViewMatrix)
.mul(this.projectionMatrix);
// Normalize screen coordinates from (-1, 1) to (0, virtualWidth) and (0, virtualHeight) // Normalize screen coordinates from (-1, 1) to (0, virtualWidth) and (0, virtualHeight)
float normalizedX = (screenCoordinates.x / screenCoordinates.w + 1.0f) * 0.5f * virtualWidth; float normalizedX = (screenCoordinates.x / screenCoordinates.w + 1.0f) * 0.5f * virtualWidth;
float normalizedY = (1.0f - screenCoordinates.y / screenCoordinates.w) * 0.5f * virtualHeight; float normalizedY = (1.0f - screenCoordinates.y / screenCoordinates.w) * 0.5f * virtualHeight;
Log.i("VertexProjection", "Projected vertex to screen coordinates: (" + normalizedX + ", " + normalizedY + ").");
return new Vector2f(normalizedX, normalizedY); return new Vector2f(normalizedX, normalizedY);
} }
@@ -137,9 +205,9 @@ public class PersonalMotionPreviewElement extends View {
* Method that converts a sequence of vectors to a Path object. * Method that converts a sequence of vectors to a Path object.
* This path is a set of bezier curves that will be drawn on the canvas. * This path is a set of bezier curves that will be drawn on the canvas.
* *
* @param segments The path segments in the path. * @param segments The path segments in the path.
* These segments will be connected by bezier curves, which * These segments will be connected by bezier curves, which
* all have unique curvature values. * all have unique curvature values.
* @return The generated path object. * @return The generated path object.
*/ */
private Path getDrawablePath(PathSegment... segments) { private Path getDrawablePath(PathSegment... segments) {
@@ -152,8 +220,8 @@ public class PersonalMotionPreviewElement extends View {
// Draw the path segments // Draw the path segments
for (PathSegment segment : segments) { for (PathSegment segment : segments) {
Vector2f startProjected = projectVertex(segment.getStart(), getWidth()/2, getHeight()); Vector2f startProjected = projectVertex(segment.getStart(), getWidth(), getHeight());
Vector2f endProjected = projectVertex(segment.getEnd(), getWidth()/2, getHeight()); Vector2f endProjected = projectVertex(segment.getEnd(), getWidth(), getHeight());
calculatedPath.lineTo(startProjected.x, startProjected.y); calculatedPath.lineTo(startProjected.x, startProjected.y);
calculatedPath.lineTo(endProjected.x, endProjected.y); calculatedPath.lineTo(endProjected.x, endProjected.y);
} }
@@ -165,8 +233,19 @@ public class PersonalMotionPreviewElement extends View {
@Override @Override
public void onDraw(Canvas canvas) { public void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundColor); canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundColor);
this.setBackgroundColor(0xFF000000); // Black
if (this.exercise == null)
return;
// Draw the sport preview canvas // Draw the sport preview canvas
canvas.drawPath(referencePath, referencePaint); canvas.drawPath(referencePath, referencePaint);
canvas.drawPath(performingPath, performingPaint); canvas.drawPath(performingPath, performingPaint);
canvas.drawText(this.exercise.getTitle(), 10, 40, textPaint);
timePassed = (System.nanoTime() - startingTime) / 1E9D;
this.rotation.add(1f, 0);
this.referencePath = getDrawablePath(this.path.getSegments());
this.invalidate(); // Causes a redraw.
} }
} }

View File

@@ -11,17 +11,12 @@ public class GesturePath {
// The vectors that make up the path. // The vectors that make up the path.
private final PathSegment[] segments; private final PathSegment[] segments;
public GesturePath(Vector3f[] vectors) {
this(vectors, 0.0D);
}
/** /**
* Create a new gesture path with a given set of vectors and curvature. * Create a new gesture path with a given set of vectors and curvature.
* *
* @param vectors The vectors that make up the path. * @param vectors The vectors that make up the path.
* @param curvature The curvature of the path.
*/ */
public GesturePath(Vector3f[] vectors, double curvature) public GesturePath(Vector3f[] vectors)
{ {
if ( vectors.length < 2) if ( vectors.length < 2)
throw new IllegalArgumentException("A path must have at least two points."); throw new IllegalArgumentException("A path must have at least two points.");

View File

@@ -11,6 +11,6 @@ public interface IMotionDataConsumer {
* @param sampleIndex The index of the current sample * @param sampleIndex The index of the current sample
* @param sampleRate The sample rate. * @param sampleRate The sample rate.
*/ */
void accept(Vector3f transformedVector, MotionData motionData, int sampleIndex, double sampleRate); void accept(Vector3f transformedVector, MotionData motionData, int sampleIndex, double sampleRate, int sensorId);
} }

View File

@@ -8,6 +8,7 @@ public class MotionData {
// Data of the motion sensor // Data of the motion sensor
public Vector3f acceleration, rotation; public Vector3f acceleration, rotation;
public int sensorId;
// Delimiter for the data received from the motion sensor // Delimiter for the data received from the motion sensor
private static final String DATA_DELIMITER = ";"; private static final String DATA_DELIMITER = ";";
@@ -21,10 +22,10 @@ public class MotionData {
* @param rotationX The rotation in the X axis in degrees. * @param rotationX The rotation in the X axis in degrees.
* @param rotationY The rotation in the Y axis in degrees. * @param rotationY The rotation in the Y axis in degrees.
* @param rotationZ The rotation in the Z axis in degrees. * @param rotationZ The rotation in the Z axis in degrees.
* @param sensorId The sensor id.
*/ */
public MotionData(float accelerationX, float accelerationY, float accelerationZ, float rotationX, float rotationY, float rotationZ) { public MotionData(float accelerationX, float accelerationY, float accelerationZ, float rotationX, float rotationY, float rotationZ, int sensorId) {
this.acceleration = new Vector3f(accelerationX, accelerationY, accelerationZ); this(new Vector3f(accelerationX, accelerationY, accelerationZ), new Vector3f(rotationX, rotationY, rotationZ), sensorId);
this.rotation = new Vector3f(rotationX, rotationY, rotationZ);
} }
/** /**
@@ -33,9 +34,10 @@ public class MotionData {
* @param acceleration The acceleration vector in m/s^2. * @param acceleration The acceleration vector in m/s^2.
* @param rotation The rotation vector in degrees. * @param rotation The rotation vector in degrees.
*/ */
public MotionData(Vector3f acceleration, Vector3f rotation) { public MotionData(Vector3f acceleration, Vector3f rotation, int sensorId) {
this.acceleration = acceleration; this.acceleration = acceleration;
this.rotation = rotation; this.rotation = rotation;
this.sensorId = sensorId;
} }
/** /**
@@ -50,7 +52,7 @@ public class MotionData {
Objects.requireNonNull(data); // Ensure data is not null Objects.requireNonNull(data); // Ensure data is not null
String[] parts = data.split(DATA_DELIMITER); String[] parts = data.split(DATA_DELIMITER);
if (parts.length != 6) if (parts.length != 7)
return null; return null;
return new MotionData( return new MotionData(
@@ -59,7 +61,8 @@ public class MotionData {
Float.parseFloat(parts[2]), Float.parseFloat(parts[2]),
Float.parseFloat(parts[3]), Float.parseFloat(parts[3]),
Float.parseFloat(parts[4]), Float.parseFloat(parts[4]),
Float.parseFloat(parts[5]) Float.parseFloat(parts[5]),
Integer.parseInt(parts[6])
); );
} }
} }

View File

@@ -22,7 +22,7 @@ public class MotionProcessor {
private Vector3f ZERO = new Vector3f(0, 0, 0); private Vector3f ZERO = new Vector3f(0, 0, 0);
private float sampleRate = 1.0F; // samples/second private float sampleRate = 1.0F; // samples/second
private IMotionDataConsumer motionDataConsumer = (p1, p2, p3, p4) -> {}; private IMotionDataConsumer motionDataConsumer = (p1, p2, p3, p4, p5) -> {};
private GesturePath path; private GesturePath path;
private WebSocket socket; private WebSocket socket;
@@ -110,7 +110,7 @@ public class MotionProcessor {
Vector3f previous = this.relativePath.isEmpty() ? ZERO : this.relativePath.get(this.relativePath.size() - 1); Vector3f previous = this.relativePath.isEmpty() ? ZERO : this.relativePath.get(this.relativePath.size() - 1);
Vector3f relativeVector = getRelativeVector(data).add(previous); Vector3f relativeVector = getRelativeVector(data).add(previous);
this.relativePath.add(relativeVector); this.relativePath.add(relativeVector);
motionDataConsumer.accept(relativeVector, data, this.relativePath.size(), this.sampleRate); motionDataConsumer.accept(relativeVector, data, this.relativePath.size(), this.sampleRate, data.sensorId);
} }
/** /**

View File

@@ -1,73 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout" android:id="@+id/drawer_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/red" android:background="#232323"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
tools:context=".ui.activities.FitnessActivity" tools:context=".ui.activities.FitnessActivity"
tools:openDrawer="start"> tools:openDrawer="start">
<!-- <com.example.fitbot.ui.components.PersonalMotionPreviewElement--> <com.example.fitbot.ui.components.PersonalMotionPreviewElement
<!-- android:id="@+id/personalMotionPreviewElement"--> android:id="@+id/personalMotionPreviewElement"
<!-- android:layout_width="600dp"--> android:layout_width="356dp"
<!-- android:layout_height="550dp"--> android:layout_height="match_parent"
<!-- app:layout_constraintBottom_toBottomOf="parent"--> android:visibility="visible" />
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- app:layout_constraintHorizontal_bias="0.976"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintTop_toTopOf="parent"-->
<!-- app:layout_constraintVertical_bias="0.064" />-->
<VideoView </android.support.v4.widget.DrawerLayout>
android:id="@+id/videoView"
android:layout_width="600dp"
android:layout_height="550dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.023"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.064" />
<Button
android:id="@+id/homeButton"
style="@style/ButtonStyle"
android:layout_width="180dp"
android:layout_height="120dp"
android:text="HOME"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.014"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<Button
android:id="@+id/skipButton"
style="@style/ButtonStyle"
android:layout_width="180dp"
android:layout_height="120dp"
android:text="SKIP"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<Button
android:id="@+id/buttonComplete"
style="@style/ButtonStyle"
android:layout_width="180dp"
android:layout_height="120dp"
android:text="Complete"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.985"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
</android.support.constraint.ConstraintLayout>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="PersonalMotionPreviewElement">
</declare-styleable>
</resources>

View File

@@ -1,3 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="Theme.Fitbot" parent="Theme.AppCompat.Light.NoActionBar"> <style name="Theme.Fitbot" parent="Theme.AppCompat.Light.NoActionBar">