From 0bdd1664dcf0800aa909572a2117ea9df64647c3 Mon Sep 17 00:00:00 2001 From: Luca Warmenhoven Date: Tue, 21 May 2024 12:31:45 +0200 Subject: [PATCH 1/4] crack cocaine and sport activities --- code/src/Fitbot/.idea/misc.xml | 1 + .../com/example/fitbot/EndScreenActivity.java | 2 + .../{AbstractExercise.java => Exercise.java} | 31 ++++- .../fitbot/exercise/ExerciseBuilder.java | 41 +++++++ .../fitbot/exercise/FitnessManager.java | 2 +- .../fitbot/ui/activities/FitnessActivity.java | 37 ++++++ .../PersonalMotionPreviewElement.java | 113 ++++++++++++------ .../example/fitbot/util/path/GesturePath.java | 7 +- .../util/processing/IMotionDataConsumer.java | 2 +- .../fitbot/util/processing/MotionData.java | 15 ++- .../util/processing/MotionProcessor.java | 4 +- .../src/main/res/layout/activity_fitness.xml | 15 +-- .../Fitbot/app/src/main/res/values/attrs.xml | 6 + .../Fitbot/app/src/main/res/values/themes.xml | 5 +- 14 files changed, 208 insertions(+), 73 deletions(-) rename code/src/Fitbot/app/src/main/java/com/example/fitbot/exercise/{AbstractExercise.java => Exercise.java} (72%) create mode 100644 code/src/Fitbot/app/src/main/java/com/example/fitbot/exercise/ExerciseBuilder.java create mode 100644 code/src/Fitbot/app/src/main/res/values/attrs.xml diff --git a/code/src/Fitbot/.idea/misc.xml b/code/src/Fitbot/.idea/misc.xml index fb7fa9e..c27764b 100644 --- a/code/src/Fitbot/.idea/misc.xml +++ b/code/src/Fitbot/.idea/misc.xml @@ -30,6 +30,7 @@ + diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/EndScreenActivity.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/EndScreenActivity.java index a12c853..3670db5 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/EndScreenActivity.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/EndScreenActivity.java @@ -5,6 +5,8 @@ import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.Button; +import com.example.fitbot.ui.activities.MainActivity; + public class EndScreenActivity extends AppCompatActivity { @Override diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/exercise/AbstractExercise.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/exercise/Exercise.java similarity index 72% rename from code/src/Fitbot/app/src/main/java/com/example/fitbot/exercise/AbstractExercise.java rename to code/src/Fitbot/app/src/main/java/com/example/fitbot/exercise/Exercise.java index daf7d8b..7beb007 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/exercise/AbstractExercise.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/exercise/Exercise.java @@ -8,14 +8,17 @@ import com.example.fitbot.util.server.WebSocket; import java.util.Objects; -public abstract class AbstractExercise implements IWebSocketHandler { +public class Exercise implements IWebSocketHandler { private EMuscleGroup muscleGroup; private GesturePath path; + private String title; + private String description; + private float segmentsPerSecond; // Static fields. 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 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.title = title; + this.description = description; this.path = path; + this.segmentsPerSecond = segmentsPerSecond; } /** @@ -101,4 +111,19 @@ public abstract class AbstractExercise implements IWebSocketHandler { public GesturePath getPath() { return path; } + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + /** + * Get the speed of the exercise. + */ + public double getSegmentsPerSecond() { + return segmentsPerSecond; + } } diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/exercise/ExerciseBuilder.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/exercise/ExerciseBuilder.java new file mode 100644 index 0000000..a770475 --- /dev/null +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/exercise/ExerciseBuilder.java @@ -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) { + + } +} diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/exercise/FitnessManager.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/exercise/FitnessManager.java index ada8292..5036a0a 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/exercise/FitnessManager.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/exercise/FitnessManager.java @@ -50,7 +50,7 @@ public class FitnessManager { * @param uniqueIdentifier The unique identifier of the exercise * @return The exercise, if it exists on the server. Otherwise null. */ - public static AbstractExercise acquireExercise(String uniqueIdentifier, Class referenceClass) { + public static Exercise acquireExercise(String uniqueIdentifier, Class referenceClass) { String response = sendHTTP( HOST_ADDRESS + "/acquire", "GET", "application/json", "{\"kind\":\"" + uniqueIdentifier + "\"}" ); diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java index 196a65b..a48cf4c 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java @@ -1,15 +1,52 @@ package com.example.fitbot.ui.activities; import android.os.Bundle; +import android.util.Log; + import com.aldebaran.qi.sdk.QiContext; import com.aldebaran.qi.sdk.RobotLifecycleCallbacks; import com.aldebaran.qi.sdk.design.activity.RobotActivity; +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 { + PersonalMotionPreviewElement personalMotionPreviewElement; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setContentView(R.layout.activity_fitness);x + + GesturePath.Builder gesturePathBuilder = new GesturePath.Builder(); + + 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)); + 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)); + 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); + }); } @Override diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java index ef111ad..c4a14cb 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java @@ -4,9 +4,11 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; +import android.util.AttributeSet; import android.util.Log; import android.view.View; +import com.example.fitbot.exercise.Exercise; import com.example.fitbot.util.path.GesturePath; import com.example.fitbot.util.path.PathSegment; import com.example.fitbot.util.processing.MotionData; @@ -22,50 +24,77 @@ public class PersonalMotionPreviewElement extends View { private GesturePath path; private double pathTime = 0.0D; // The timestamp at which the path is currently at. private MotionProcessor motionProcessor; - private Path referencePath, performingPath; - private Paint referencePaint, performingPaint; + + 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 backgroundColor = new Paint(); /** * 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_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 cameraPosition = new Vector3f(0.0f, 0.0f, -1.5f); // The position of the camera private Vector2f screenDimensions = new Vector2f(); // Width and height dimensions of the screen private Vector2f rotation = new Vector2f(); // Rotation vector (yaw, pitch) + public PersonalMotionPreviewElement(Context context, AttributeSet attrs) { + super(context, attrs); + + this.referencePaint = new Paint(); + this.referencePaint.setColor(0xFFFF0000); // Red + this.referencePaint.setStyle(Paint.Style.STROKE); + this.referencePaint.setStrokeWidth(5.0f); + + this.performingPaint = new Paint(); + this.performingPaint.setColor(0xFF0000FF); // Blue + this.performingPaint.setStyle(Paint.Style.STROKE); + this.performingPaint.setStrokeWidth(5.0f); + } + + private void updateStickmanGestures() { + // Reset previous path + stickmanPath.reset(); + + 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 + }; + + // Generate new path for stickman + + } + /** - * Constructor for the PersonalMotionPreviewElement class. + * Method for initializing the PersonalMotionPreviewElement. * - * @param context The context in which this element is created. - * @param path The gesture path that will be drawn on the canvas. + * @param exercise The exercise that the user is currently performing. */ - public PersonalMotionPreviewElement(Context context, GesturePath path) { - super(context); + 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.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.setColor(-1); // White - this.referencePaint.setStyle(Paint.Style.STROKE); - this.referencePaint.setStrokeWidth(5.0f); - this.performingPaint = new Paint(); - this.performingPaint.setColor(0xFF0000FF); // Blue - this.performingPaint.setStyle(Paint.Style.STROKE); - this.performingPaint.setStrokeWidth(5.0f); } /** @@ -90,7 +119,7 @@ public class PersonalMotionPreviewElement extends View { /** * 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. */ public void setRotation(float yaw, float pitch) { @@ -98,28 +127,27 @@ public class PersonalMotionPreviewElement extends View { } /** - * 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. + * 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. * - * @param point The point to cast to the screen. - * @param virtualWidth The width of the virtual screen. - * This is used to normalize the screen coordinates. + * @param point The point to cast to the screen. + * @param virtualWidth The width of the virtual screen. + * This is used to normalize the screen coordinates. * @param virtualHeight The height of the virtual screen. * @return The transformed vector in screen coordinates ranging from (0, 0) to (virtualWidth, virtualHeight). */ private Vector2f projectVertex(Vector3f point, int virtualWidth, int virtualHeight) { - + Log.i("VertexProjection", "Projecting vertex to screen coordinates: " + point.toString() + " with virtual width " + virtualWidth + " and virtual height " + virtualHeight + "."); Matrix4f modelViewMatrix = new Matrix4f() - .rotateX((float) Math.toRadians(rotation.x)) - .rotateY((float) Math.toRadians(rotation.y)) - .translate(cameraPosition); + .translate(-cameraPosition.x, -cameraPosition.y, -cameraPosition.z) + .rotateX((float) Math.toRadians(rotation.y)) + .rotateY((float) Math.toRadians(rotation.x)); Matrix4f projectionMatrix = new Matrix4f() .perspective((float) Math.toRadians(FOV), (float) virtualWidth / virtualHeight, Z_NEAR, Z_FAR); // Calculate Model-View-Projection matrix - Matrix4f MVP = new Matrix4f() - .set(projectionMatrix) + Matrix4f MVP = new Matrix4f(projectionMatrix) .mul(modelViewMatrix); // Convert to screen coordinates @@ -129,7 +157,7 @@ public class PersonalMotionPreviewElement extends View { // Normalize screen coordinates from (-1, 1) to (0, virtualWidth) and (0, virtualHeight) float normalizedX = (screenCoordinates.x / screenCoordinates.w + 1.0f) * 0.5f * virtualWidth; 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); } @@ -137,9 +165,9 @@ public class PersonalMotionPreviewElement extends View { * 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. * - * @param segments The path segments in the path. - * These segments will be connected by bezier curves, which - * all have unique curvature values. + * @param segments The path segments in the path. + * These segments will be connected by bezier curves, which + * all have unique curvature values. * @return The generated path object. */ private Path getDrawablePath(PathSegment... segments) { @@ -152,8 +180,8 @@ public class PersonalMotionPreviewElement extends View { // Draw the path segments for (PathSegment segment : segments) { - Vector2f startProjected = projectVertex(segment.getStart(), getWidth()/2, getHeight()); - Vector2f endProjected = projectVertex(segment.getEnd(), getWidth()/2, getHeight()); + Vector2f startProjected = projectVertex(segment.getStart(), getWidth(), getHeight()); + Vector2f endProjected = projectVertex(segment.getEnd(), getWidth(), getHeight()); calculatedPath.lineTo(startProjected.x, startProjected.y); calculatedPath.lineTo(endProjected.x, endProjected.y); } @@ -165,8 +193,15 @@ public class PersonalMotionPreviewElement extends View { @Override public void onDraw(Canvas canvas) { canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundColor); + this.setBackgroundColor(0xFF000000); // Black + if (path == null) + return; // Draw the sport preview canvas canvas.drawPath(referencePath, referencePaint); canvas.drawPath(performingPath, performingPaint); + + this.rotation.add(1f, 0); + this.referencePath = getDrawablePath(this.path.getSegments()); + this.invalidate(); } } diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/path/GesturePath.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/path/GesturePath.java index 88c41ad..61229c2 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/path/GesturePath.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/path/GesturePath.java @@ -11,17 +11,12 @@ public class GesturePath { // The vectors that make up the path. 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. * * @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) throw new IllegalArgumentException("A path must have at least two points."); diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/IMotionDataConsumer.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/IMotionDataConsumer.java index b09726d..dd3220c 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/IMotionDataConsumer.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/IMotionDataConsumer.java @@ -11,6 +11,6 @@ public interface IMotionDataConsumer { * @param sampleIndex The index of the current sample * @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); } \ No newline at end of file diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionData.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionData.java index 37e016f..fae42fd 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionData.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionData.java @@ -8,6 +8,7 @@ public class MotionData { // Data of the motion sensor public Vector3f acceleration, rotation; + public int sensorId; // Delimiter for the data received from the motion sensor private static final String DATA_DELIMITER = ";"; @@ -21,10 +22,10 @@ public class MotionData { * @param rotationX The rotation in the X axis in degrees. * @param rotationY The rotation in the Y 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) { - this.acceleration = new Vector3f(accelerationX, accelerationY, accelerationZ); - this.rotation = new Vector3f(rotationX, rotationY, rotationZ); + public MotionData(float accelerationX, float accelerationY, float accelerationZ, float rotationX, float rotationY, float rotationZ, int sensorId) { + this(new Vector3f(accelerationX, accelerationY, accelerationZ), new Vector3f(rotationX, rotationY, rotationZ), sensorId); } /** @@ -33,9 +34,10 @@ public class MotionData { * @param acceleration The acceleration vector in m/s^2. * @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.rotation = rotation; + this.sensorId = sensorId; } /** @@ -50,7 +52,7 @@ public class MotionData { Objects.requireNonNull(data); // Ensure data is not null String[] parts = data.split(DATA_DELIMITER); - if (parts.length != 6) + if (parts.length != 7) return null; return new MotionData( @@ -59,7 +61,8 @@ public class MotionData { Float.parseFloat(parts[2]), Float.parseFloat(parts[3]), Float.parseFloat(parts[4]), - Float.parseFloat(parts[5]) + Float.parseFloat(parts[5]), + Integer.parseInt(parts[6]) ); } } diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionProcessor.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionProcessor.java index 9a04461..b5d2b2b 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionProcessor.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionProcessor.java @@ -22,7 +22,7 @@ public class MotionProcessor { private Vector3f ZERO = new Vector3f(0, 0, 0); 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 WebSocket socket; @@ -110,7 +110,7 @@ public class MotionProcessor { Vector3f previous = this.relativePath.isEmpty() ? ZERO : this.relativePath.get(this.relativePath.size() - 1); Vector3f relativeVector = getRelativeVector(data).add(previous); this.relativePath.add(relativeVector); - motionDataConsumer.accept(relativeVector, data, this.relativePath.size(), this.sampleRate); + motionDataConsumer.accept(relativeVector, data, this.relativePath.size(), this.sampleRate, data.sensorId); } /** diff --git a/code/src/Fitbot/app/src/main/res/layout/activity_fitness.xml b/code/src/Fitbot/app/src/main/res/layout/activity_fitness.xml index 38aa5c9..fb50c4e 100644 --- a/code/src/Fitbot/app/src/main/res/layout/activity_fitness.xml +++ b/code/src/Fitbot/app/src/main/res/layout/activity_fitness.xml @@ -1,6 +1,6 @@ - - - - + android:layout_width="356dp" + android:layout_height="match_parent" + android:visibility="visible" /> \ No newline at end of file diff --git a/code/src/Fitbot/app/src/main/res/values/attrs.xml b/code/src/Fitbot/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000..29e5394 --- /dev/null +++ b/code/src/Fitbot/app/src/main/res/values/attrs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/code/src/Fitbot/app/src/main/res/values/themes.xml b/code/src/Fitbot/app/src/main/res/values/themes.xml index 14f6b4a..fbef5ec 100644 --- a/code/src/Fitbot/app/src/main/res/values/themes.xml +++ b/code/src/Fitbot/app/src/main/res/values/themes.xml @@ -1,3 +1,4 @@ + \ No newline at end of file From 64412a849afb06883b84c710738f698d0197033e Mon Sep 17 00:00:00 2001 From: Luca Warmenhoven Date: Wed, 22 May 2024 10:51:41 +0300 Subject: [PATCH 2/4] store --- .../java/com/example/fitbot/ui/activities/FitnessActivity.java | 2 +- .../fitbot/ui/components/PersonalMotionPreviewElement.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java index a48cf4c..e4c1840 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java @@ -21,7 +21,7 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_fitness);x + setContentView(R.layout.activity_fitness); GesturePath.Builder gesturePathBuilder = new GesturePath.Builder(); diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java index c4a14cb..de169c4 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java @@ -92,7 +92,7 @@ public class PersonalMotionPreviewElement extends View { this.path = path; this.motionProcessor = new MotionProcessor(); this.motionProcessor.startListening(); - this.motionProcessor.setMotionDataEventHandler((processed, preprocessed, sampleIndex, sampleRate) -> { + this.motionProcessor.setMotionDataEventHandler((processed, preprocessed, sampleIndex, sampleRate, deviceId) -> { // TODO: Implement the calculation of the `performingPath` based on the motion data }); } From ccaf302558636e3505e94c86238a63418c8158da Mon Sep 17 00:00:00 2001 From: Luca Warmenhoven Date: Wed, 22 May 2024 11:01:18 +0300 Subject: [PATCH 3/4] store --- .../Luca/Skills Ontwikkelings Plan.docx | Bin 0 -> 11209 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/personal-documentation/Luca/Skills Ontwikkelings Plan.docx diff --git a/docs/personal-documentation/Luca/Skills Ontwikkelings Plan.docx b/docs/personal-documentation/Luca/Skills Ontwikkelings Plan.docx new file mode 100644 index 0000000000000000000000000000000000000000..28a85614de345d372d517cac957f49b97a6b1c94 GIT binary patch literal 11209 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 Date: Wed, 22 May 2024 11:48:02 +0300 Subject: [PATCH 4/4] Updated PersonalMotionPreviewElement and FitnessActivity to match functionality --- .../fitbot/ui/activities/FitnessActivity.java | 14 ++-- .../PersonalMotionPreviewElement.java | 74 +++++++++++++++---- 2 files changed, 64 insertions(+), 24 deletions(-) diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java index d7f2460..2fca666 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/activities/FitnessActivity.java @@ -1,7 +1,10 @@ package com.example.fitbot.ui.activities; +import android.content.Context; import android.os.Bundle; +import android.util.AttributeSet; import android.util.Log; +import android.view.View; import com.aldebaran.qi.sdk.QiContext; import com.aldebaran.qi.sdk.QiSDK; @@ -42,7 +45,6 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall gesturePathBuilder.addVector(new Vector3f(-.5f, .5f, -.5f)); - personalMotionPreviewElement = findViewById(R.id.personalMotionPreviewElement); personalMotionPreviewElement.post(() -> { Log.i("FitnessActivity", "PersonalMotionPreviewElement.post()"); @@ -56,7 +58,7 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall @Override public void onRobotFocusGained(QiContext qiContext) { // Implement your logic when the robot focus is gained - Animate("bicepcurl", qiContext); + Animations.Animate("bicepcurl", qiContext); } @@ -76,10 +78,4 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall super.onDestroy(); } - public static class PersonalMotionPreviewElement extends View { - - public PersonalMotionPreviewElement(Context context, AttributeSet attrs) { - super(context, attrs); - // Initialize your custom view here (optional) - } - } +} diff --git a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java index de169c4..d482362 100644 --- a/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java +++ b/code/src/Fitbot/app/src/main/java/com/example/fitbot/ui/components/PersonalMotionPreviewElement.java @@ -25,12 +25,23 @@ public class PersonalMotionPreviewElement extends View { private double pathTime = 0.0D; // The timestamp at which the path is currently at. private MotionProcessor motionProcessor; + 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(); @@ -40,7 +51,7 @@ public class PersonalMotionPreviewElement extends View { 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_FAR = 1000.0f; // The far clipping plane - private Vector3f cameraPosition = new Vector3f(0.0f, 0.0f, -1.5f); // 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 rotation = new Vector2f(); // Rotation vector (yaw, pitch) @@ -56,12 +67,34 @@ public class PersonalMotionPreviewElement extends View { this.performingPaint.setColor(0xFF0000FF); // Blue this.performingPaint.setStyle(Paint.Style.STROKE); 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 @@ -70,12 +103,15 @@ public class PersonalMotionPreviewElement extends View { new PathSegment(new Vector3f(.25f, .25f, 0f), new Vector3f(0, 0, 0)) // Right arm }; - // Generate new path for stickman + // 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. */ @@ -89,7 +125,10 @@ public class PersonalMotionPreviewElement extends View { this.performingPath = new Path(); this.referencePath = new Path(); - this.path = 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) -> { @@ -137,22 +176,23 @@ public class PersonalMotionPreviewElement extends View { * @return The transformed vector in screen coordinates ranging from (0, 0) to (virtualWidth, virtualHeight). */ private Vector2f projectVertex(Vector3f point, int virtualWidth, int virtualHeight) { - Log.i("VertexProjection", "Projecting vertex to screen coordinates: " + point.toString() + " with virtual width " + virtualWidth + " and virtual height " + virtualHeight + "."); - Matrix4f modelViewMatrix = new Matrix4f() - .translate(-cameraPosition.x, -cameraPosition.y, -cameraPosition.z) + + modelViewMatrix + .identity() + .translate(-objectPosition.x, -objectPosition.y, -objectPosition.z) .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); - // Calculate Model-View-Projection matrix - Matrix4f MVP = new Matrix4f(projectionMatrix) - .mul(modelViewMatrix); - - // Convert to screen coordinates + // Convert world coordinates to screen-space using MVP matrix 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) float normalizedX = (screenCoordinates.x / screenCoordinates.w + 1.0f) * 0.5f * virtualWidth; @@ -194,14 +234,18 @@ public class PersonalMotionPreviewElement extends View { public void onDraw(Canvas canvas) { canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundColor); this.setBackgroundColor(0xFF000000); // Black - if (path == null) + if (this.exercise == null) return; // Draw the sport preview canvas canvas.drawPath(referencePath, referencePaint); 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(); + this.invalidate(); // Causes a redraw. } }