Time estimation menu

This commit is contained in:
YTKAB0BP
2024-11-15 03:26:33 +03:00
parent 7f56cba79d
commit 53e20fdaae
10 changed files with 617 additions and 21 deletions
@@ -5,11 +5,13 @@ import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -22,6 +24,7 @@ import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
import androidx.core.util.Pair;
import com.google.android.material.checkbox.MaterialCheckBox;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.http.RequestParams;
@@ -51,10 +54,13 @@ import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.slic3r.GCodeViewer;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
import ru.ytkab0bp.slicebeam.theme.IThemeView;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.DividerView;
import ru.ytkab0bp.slicebeam.view.PositionScrollView;
import ru.ytkab0bp.slicebeam.view.SegmentsView;
public class SliceMenu extends ListBedMenu {
private AsyncHttpClient client = new AsyncHttpClient();
@@ -71,7 +77,8 @@ public class SliceMenu extends ListBedMenu {
protected List<SimpleRecyclerItem> onCreateItems(boolean portrait) {
lastUid = SliceBeam.CONFIG_UID;
List<SimpleRecyclerItem> items = new ArrayList<>(Arrays.asList(
new BedMenuItem(R.string.MenuSliceInfo, R.drawable.square_stack_up_outline_28).onClick(v -> fragment.showUnfoldMenu(new LayersMenu(), v)),
new BedMenuItem(R.string.MenuSliceInfo, R.drawable.clock_circle_dashed_outline_24).onClick(v -> fragment.showUnfoldMenu(new InfoMenu(), v)),
new BedMenuItem(R.string.MenuSliceLayers, R.drawable.square_stack_up_outline_28).onClick(v -> fragment.showUnfoldMenu(new LayersMenu(), v)),
new BedMenuItem(R.string.MenuSliceExportToFile, R.drawable.folder_simple_arrow_right_outline_28).onClick(v -> {
if (fragment.getContext() instanceof Activity) {
Activity act = (Activity) fragment.getContext();
@@ -176,15 +183,305 @@ public class SliceMenu extends ListBedMenu {
});
}
private final static class InfoMenu extends UnfoldMenu implements IThemeView {
private TextView totalView;
private SegmentsView segmentsView;
private ExtrusionRoleView[] roleViews = new ExtrusionRoleView[GCodeViewer.EXTRUSION_ROLES_COUNT];
private GCodeViewer getViewer() {
return fragment.getGlView().getRenderer().getViewer();
}
@Override
protected View onCreateView(Context ctx, boolean portrait) {
LinearLayout ll = new LinearLayout(ctx);
ll.setOrientation(LinearLayout.VERTICAL);
ll.addView(new Space(ctx), new LinearLayout.LayoutParams(0, 0, 1f));
for (int i = 0; i < GCodeViewer.EXTRUSION_ROLES_COUNT; i++) {
ll.addView(roleViews[i] = new ExtrusionRoleView(ctx));
}
ll.addView(new DividerView(ctx), new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(1f)));
totalView = new TextView(ctx);
totalView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
totalView.setGravity(Gravity.CENTER);
ll.addView(totalView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(18)) {{
topMargin = ViewUtils.dp(8);
}});
segmentsView = new SegmentsView(ctx);
ll.addView(segmentsView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(12)) {{
leftMargin = rightMargin = ViewUtils.dp(12);
topMargin = bottomMargin = ViewUtils.dp(8);
}});
ll.addView(new DividerView(ctx), new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(1f)));
LinearLayout toolbar = new LinearLayout(ctx);
toolbar.setPadding(ViewUtils.dp(12), 0, ViewUtils.dp(12), 0);
toolbar.setOrientation(LinearLayout.HORIZONTAL);
toolbar.setGravity(Gravity.CENTER_VERTICAL);
toolbar.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 0));
toolbar.setOnClickListener(v -> dismiss());
ImageView icon = new ImageView(ctx);
icon.setImageResource(R.drawable.arrow_left_outline_28);
icon.setColorFilter(ThemesRepo.getColor(android.R.attr.textColorSecondary));
toolbar.addView(icon, new LinearLayout.LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28)));
TextView title = new TextView(ctx);
title.setText(R.string.MenuOrientationPositionBack);
title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
title.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
toolbar.addView(title, new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) {{
leftMargin = ViewUtils.dp(12);
}});
ll.addView(toolbar, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)));
onApplyTheme();
return ll;
}
@Override
protected void onCreate() {
super.onCreate();
GCodeViewer viewer = getViewer();
if (viewer != null) {
for (int i = 0; i < GCodeViewer.EXTRUSION_ROLES_COUNT; i++) {
boolean visible = viewer.getEstimatedTime(i) != 0;
roleViews[i].setVisibility(visible ? View.VISIBLE : View.GONE);
if (visible) {
roleViews[i].bind(viewer, i);
}
}
}
updateValues();
ViewUtils.postOnMainThread(() -> segmentsView.startAnimation(), 50);
}
@Override
protected void onDestroy() {
super.onDestroy();
segmentsView.setNotVisible();
}
@Override
public int getRequestedSize(FrameLayout into, boolean portrait) {
if (portrait) {
GCodeViewer viewer = getViewer();
if (viewer != null) {
int visibleCount = 0;
for (int i = 0; i < GCodeViewer.EXTRUSION_ROLES_COUNT; i++) {
if (viewer.getEstimatedTime(i) != 0) {
visibleCount++;
}
}
return ViewUtils.dp(42) * visibleCount + ViewUtils.dp(28) + ViewUtils.dp(52) + ViewUtils.dp(18 + 8);
}
}
return super.getRequestedSize(into, portrait);
}
private float getTotalEstimatedTime() {
GCodeViewer viewer = getViewer();
if (viewer == null) return 0;
float total = 0;
for (int i = 0; i < GCodeViewer.EXTRUSION_ROLES_COUNT; i++) {
if (viewer.isExtrusionRoleVisible(i)) {
total += viewer.getEstimatedTime(i);
}
}
return total;
}
private void updateValues() {
GCodeViewer viewer = getViewer();
if (viewer == null) return;
float[] values = new float[2 + GCodeViewer.EXTRUSION_ROLES_COUNT];
values[0] = 0;
values[values.length - 1] = 1;
float prev = 0;
int lastVisible = 0;
float total = getTotalEstimatedTime();
for (int i = 0; i < GCodeViewer.EXTRUSION_ROLES_COUNT; i++) {
if (viewer.isExtrusionRoleVisible(i)) {
float percent = viewer.getEstimatedTime(i) / total;
values[i + 1] = prev + percent;
lastVisible = i;
prev = values[i + 1];
} else {
values[i + 1] = prev;
}
}
values[lastVisible] = 1;
segmentsView.setValues(values);
totalView.setText(formatTime(total));
}
private static String formatTime(float time) {
int secondsTotal = Math.round(time);
int seconds = secondsTotal % 60;
int minutes = ((secondsTotal - seconds) / 60) % 60;
int hours = ((secondsTotal - seconds) / 60) / 60;
StringBuilder sb = new StringBuilder();
if (hours > 0) {
sb.append(hours).append(" ").append(SliceBeam.INSTANCE.getString(R.string.MenuSliceInfoHour));
}
if (minutes > 0) {
if (sb.length() > 0) sb.append(" ");
sb.append(minutes).append(" ").append(SliceBeam.INSTANCE.getString(R.string.MenuSliceInfoMinute));
}
if (seconds > 0) {
if (sb.length() > 0) sb.append(" ");
sb.append(seconds).append(" ").append(SliceBeam.INSTANCE.getString(R.string.MenuSliceInfoSecond));
}
return sb.toString();
}
@Override
public void onApplyTheme() {
totalView.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
}
private final class ExtrusionRoleView extends LinearLayout implements IThemeView {
private MaterialCheckBox checkBox;
private TextView titleView;
private TextView timeView;
private Runnable invalidateGl;
public ExtrusionRoleView(Context context) {
super(context);
setOrientation(HORIZONTAL);
setGravity(Gravity.CENTER_VERTICAL);
setPadding(ViewUtils.dp(12), 0, ViewUtils.dp(16), 0);
checkBox = new MaterialCheckBox(getContext()) {
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
return false;
}
};
addView(checkBox, new LinearLayout.LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28)));
titleView = new TextView(context);
titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
addView(titleView, new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) {{
leftMargin = rightMargin = ViewUtils.dp(12);
}});
timeView = new TextView(context);
timeView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
addView(timeView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(42)));
onApplyTheme();
}
public void bind(GCodeViewer viewer, @GCodeViewer.ExtrusionRole int role) {
switch (role) {
case GCodeViewer.EXTRUSION_ROLE_NONE:
titleView.setText(Slic3rLocalization.getString("Unknown"));
break;
case GCodeViewer.EXTRUSION_ROLE_PERIMETER:
titleView.setText(Slic3rLocalization.getString("Perimeter"));
break;
case GCodeViewer.EXTRUSION_ROLE_EXTERNAL_PERIMETER:
titleView.setText(Slic3rLocalization.getString("External perimeter"));
break;
case GCodeViewer.EXTRUSION_ROLE_OVERHANG_PERIMETER:
titleView.setText(Slic3rLocalization.getString("Overhang perimeter"));
break;
case GCodeViewer.EXTRUSION_ROLE_INTERNAL_INFILL:
titleView.setText(Slic3rLocalization.getString("Internal infill"));
break;
case GCodeViewer.EXTRUSION_ROLE_SOLID_INFILL:
titleView.setText(Slic3rLocalization.getString("Solid infill"));
break;
case GCodeViewer.EXTRUSION_ROLE_TOP_SOLID_INFILL:
titleView.setText(Slic3rLocalization.getString("Top solid infill"));
break;
case GCodeViewer.EXTRUSION_ROLE_IRONING:
titleView.setText(Slic3rLocalization.getString("Ironing"));
break;
case GCodeViewer.EXTRUSION_ROLE_BRIDGE_INFILL:
titleView.setText(Slic3rLocalization.getString("Bridge infill"));
break;
case GCodeViewer.EXTRUSION_ROLE_GAP_FILL:
titleView.setText(Slic3rLocalization.getString("Gap fill"));
break;
case GCodeViewer.EXTRUSION_ROLE_SKIRT:
titleView.setText(Slic3rLocalization.getString("Skirt/Brim"));
break;
case GCodeViewer.EXTRUSION_ROLE_SUPPORT_MATERIAL:
titleView.setText(Slic3rLocalization.getString("Support material"));
break;
case GCodeViewer.EXTRUSION_ROLE_SUPPORT_MATERIAL_INTERFACE:
titleView.setText(Slic3rLocalization.getString("Support material interface"));
break;
case GCodeViewer.EXTRUSION_ROLE_WIPE_TOWER:
titleView.setText(Slic3rLocalization.getString("Wipe tower"));
break;
case GCodeViewer.EXTRUSION_ROLE_CUSTOM:
titleView.setText(Slic3rLocalization.getString("Custom"));
break;
}
timeView.setText(formatTime(viewer.getEstimatedTime(role)));
checkBox.setChecked(viewer.isExtrusionRoleVisible(role));
checkBox.setButtonTintList(ColorStateList.valueOf(SegmentsView.mapColor(role)));
setOnClickListener(v -> {
if (getTotalEstimatedTime() == viewer.getEstimatedTime(role)) {
return;
}
viewer.toggleExtrusionRoleVisible(role);
checkBox.setChecked(!checkBox.isChecked());
updateValues();
if (invalidateGl != null) ViewUtils.removeCallbacks(invalidateGl);
ViewUtils.postOnMainThread(invalidateGl = () -> {
Pair<Long, Long> p = viewer.getLayersViewRange();
viewer.setLayersViewRange(p.first, p.second);
fragment.getGlView().requestRender();
}, 250);
});
}
@Override
public void onApplyTheme() {
titleView.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
timeView.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 12));
}
}
}
private final static class LayersMenu extends UnfoldMenu {
private PositionScrollView fromTrack, toTrack;
private TextView title;
private Runnable applyCallback;
private GCodeViewer getViewer() {
return fragment.getGlView().getRenderer().getViewer();
}
private void applyView(int from, int to) {
if (applyCallback != null) ViewUtils.removeCallbacks(applyCallback);
GCodeViewer viewer = getViewer();
if (viewer == null) {
return;
@@ -218,15 +515,20 @@ public class SliceMenu extends ListBedMenu {
if (toTrack.getCurrentPosition() < integer) {
toTrack.setCurrentPosition(integer);
}
applyView(integer, toTrack.getCurrentPosition());
title.setText(fragment.getContext().getString(R.string.MenuSliceInfoLayers, fromTrack.getCurrentPosition(), integer));
ViewUtils.removeCallbacks(applyCallback);
ViewUtils.postOnMainThread(applyCallback = ()-> applyView(integer, toTrack.getCurrentPosition()), 50);
});
fromTrack.setListener(integer -> applyView(integer, toTrack.getCurrentPosition()));
ll.addView(fromTrack, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(80)));
toTrack = new PositionScrollView(ctx);
toTrack.setProgressListener(integer -> {
// TODO: apply only visual?
applyView(fromTrack.getCurrentPosition(), integer);
title.setText(fragment.getContext().getString(R.string.MenuSliceInfoLayers, fromTrack.getCurrentPosition(), integer));
ViewUtils.removeCallbacks(applyCallback);
ViewUtils.postOnMainThread(applyCallback = ()-> applyView(fromTrack.getCurrentPosition(), integer), 50);
});
toTrack.setListener(integer -> applyView(fromTrack.getCurrentPosition(), integer));
ll.addView(toTrack, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(80)));
@@ -267,6 +569,7 @@ public class SliceMenu extends ListBedMenu {
super.onCreate();
GCodeViewer viewer = getViewer();
if (viewer == null) return;
long max = viewer.getLayersCount();
fromTrack.setMinMax(1, (int) max);
toTrack.setMinMax(1, (int) max);
@@ -285,9 +588,6 @@ public class SliceMenu extends ListBedMenu {
fromTrack.stopScroll();
toTrack.stopScroll();
if (getViewer() != null) {
applyView(1, (int) getViewer().getLayersCount());
}
}
}
}
@@ -472,6 +472,11 @@ public class BedFragment extends Fragment {
super.onApplyTheme();
menuView.setBackgroundColor(ThemesRepo.getColor(android.R.attr.windowBackground));
for (int i = 0; i < MenuCategory.values().length; i++) {
if (i != currentMenuSlot) {
ThemesRepo.invalidateView(menuMap.get(i).getView());
}
}
}
private void constructMenuView(Context ctx, boolean portrait) {
@@ -4,12 +4,54 @@ import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
import android.graphics.Color;
import androidx.annotation.IntDef;
import androidx.core.util.Pair;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
public class GCodeViewer {
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
EXTRUSION_ROLE_NONE,
EXTRUSION_ROLE_PERIMETER,
EXTRUSION_ROLE_EXTERNAL_PERIMETER,
EXTRUSION_ROLE_OVERHANG_PERIMETER,
EXTRUSION_ROLE_INTERNAL_INFILL,
EXTRUSION_ROLE_SOLID_INFILL,
EXTRUSION_ROLE_TOP_SOLID_INFILL,
EXTRUSION_ROLE_IRONING,
EXTRUSION_ROLE_BRIDGE_INFILL,
EXTRUSION_ROLE_GAP_FILL,
EXTRUSION_ROLE_SKIRT,
EXTRUSION_ROLE_SUPPORT_MATERIAL,
EXTRUSION_ROLE_SUPPORT_MATERIAL_INTERFACE,
EXTRUSION_ROLE_WIPE_TOWER,
EXTRUSION_ROLE_CUSTOM
})
public @interface ExtrusionRole{}
public final static int EXTRUSION_ROLE_NONE = 0,
EXTRUSION_ROLE_PERIMETER = 1,
EXTRUSION_ROLE_EXTERNAL_PERIMETER = 2,
EXTRUSION_ROLE_OVERHANG_PERIMETER = 3,
EXTRUSION_ROLE_INTERNAL_INFILL = 4,
EXTRUSION_ROLE_SOLID_INFILL = 5,
EXTRUSION_ROLE_TOP_SOLID_INFILL = 6,
EXTRUSION_ROLE_IRONING = 7,
EXTRUSION_ROLE_BRIDGE_INFILL = 8,
EXTRUSION_ROLE_GAP_FILL = 9,
EXTRUSION_ROLE_SKIRT = 10,
EXTRUSION_ROLE_SUPPORT_MATERIAL = 11,
EXTRUSION_ROLE_SUPPORT_MATERIAL_INTERFACE = 12,
EXTRUSION_ROLE_WIPE_TOWER = 13,
EXTRUSION_ROLE_CUSTOM = 14,
EXTRUSION_ROLES_COUNT = 15;
private ThreadLocal<float[]> viewMatrixBuffer = new ThreadLocal<float[]>() {
@Override
protected float[] initialValue() {
@@ -54,6 +96,22 @@ public class GCodeViewer {
return Native.vgcode_get_layers_count(pointer);
}
public float getEstimatedTime() {
return Native.vgcode_get_estimated_time(pointer);
}
public float getEstimatedTime(@ExtrusionRole int extrusionRole) {
return Native.vgcode_get_estimated_time_role(pointer, extrusionRole);
}
public boolean isExtrusionRoleVisible(@ExtrusionRole int extrusionRole) {
return Native.vgcode_is_extrusion_role_visible(pointer, extrusionRole);
}
public void toggleExtrusionRoleVisible(@ExtrusionRole int extrusionRole) {
Native.vgcode_toggle_extrusion_role_visibility(pointer, extrusionRole);
}
public void render(double[] viewMatrix, double[] projectionMatrix) {
assertTrue(viewMatrix.length == 16);
assertTrue(projectionMatrix.length == 16);
@@ -84,6 +84,10 @@ class Native {
static native void vgcode_render(long ptr, float[] viewMatrix, float[] projectionMatrix);
static native void vgcode_set_layers_view_range(long ptr, long min, long max);
static native long[] vgcode_get_layers_view_range(long ptr);
static native float vgcode_get_estimated_time(long ptr);
static native float vgcode_get_estimated_time_role(long ptr, int role);
static native boolean vgcode_is_extrusion_role_visible(long ptr, int role);
static native void vgcode_toggle_extrusion_role_visibility(long ptr, int role);
static native void vgcode_reset(long ptr);
static native void vgcode_release(long ptr);
@@ -27,13 +27,21 @@ public class BeamTheme {
colors.put(R.attr.modelHoverColor, 0xffffffff);
colors.put(R.attr.textColorNegative, 0xffff464a);
colors.put(R.attr.gcodeViewerSkirt, 0x7FFF7F);
colors.put(R.attr.gcodeViewerExternalPerimeter, 0xFFFF00);
colors.put(R.attr.gcodeViewerSupportMaterial, 0x7FFF7F);
colors.put(R.attr.gcodeViewerSupportMaterialInterface, 0x7FFF7F);
colors.put(R.attr.gcodeViewerInternalInfill, 0xFF7F7F);
colors.put(R.attr.gcodeViewerSolidInfill, 0xFF7F7F);
colors.put(R.attr.gcodeViewerWipeTower, 0xF7FF7F);
colors.put(R.attr.gcodeViewerNone, 0xFFE6B3B3);
colors.put(R.attr.gcodeViewerPerimeter, 0xFFFFE64D);
colors.put(R.attr.gcodeViewerExternalPerimeter, 0xFFFF7D38);
colors.put(R.attr.gcodeViewerOverhangPerimeter, 0xFF1F1FFF);
colors.put(R.attr.gcodeViewerInternalInfill, 0xFFB03029);
colors.put(R.attr.gcodeViewerSolidInfill, 0xFF9654CC);
colors.put(R.attr.gcodeViewerTopSolidInfill, 0xFFF04040);
colors.put(R.attr.gcodeViewerIroning, 0xFFFF8C69);
colors.put(R.attr.gcodeViewerBridgeInfill, 0xFF4D80BA);
colors.put(R.attr.gcodeViewerGapFill, 0xFF00876E);
colors.put(R.attr.gcodeViewerSkirt, 0xFF00876E);
colors.put(R.attr.gcodeViewerSupportMaterial, 0xFF00FF00);
colors.put(R.attr.gcodeViewerSupportMaterialInterface, 0xFF008000);
colors.put(R.attr.gcodeViewerWipeTower, 0xFFB3E3AB);
colors.put(R.attr.gcodeViewerCustom, 0xFF5ED194);
colors.put(R.attr.xTrackColor, 0xffbf0000);
colors.put(R.attr.yTrackColor, 0xff00bf00);
@@ -0,0 +1,134 @@
package ru.ytkab0bp.slicebeam.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.dynamicanimation.animation.FloatValueHolder;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import java.util.Arrays;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.slic3r.GCodeViewer;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public class SegmentsView extends View {
private Path path = new Path();
private Paint paint = new Paint();
private float showProgress;
private boolean isVisible;
private float[] currentValues;
private SpringAnimation currentChangeAnimation;
public SegmentsView(Context context) {
super(context);
}
public void startAnimation() {
isVisible = true;
new SpringAnimation(new FloatValueHolder(0))
.setMinimumVisibleChange(1 / 256f)
.setSpring(new SpringForce(1f)
.setStiffness(1000f)
.setDampingRatio(1f))
.addUpdateListener((animation, value, velocity) -> {
showProgress = value;
invalidate();
})
.start();
}
public void setNotVisible() {
isVisible = false;
}
public void setValues(float[] values) {
if (!isVisible || currentValues == null || currentValues.length != values.length) {
currentValues = values;
invalidate();
} else {
float[] prevValues = Arrays.copyOf(currentValues, currentValues.length);
currentChangeAnimation = new SpringAnimation(new FloatValueHolder(0))
.setMinimumVisibleChange(1 / 256f)
.setSpring(new SpringForce(1f)
.setStiffness(1000f)
.setDampingRatio(1f))
.addUpdateListener((animation, value, velocity) -> {
for (int i = 0; i < currentValues.length; i++) {
currentValues[i] = ViewUtils.lerp(prevValues[i], values[i], value);
}
invalidate();
})
.addEndListener((animation, canceled, value, velocity) -> {
if (animation == currentChangeAnimation) {
currentChangeAnimation = null;
}
});
currentChangeAnimation.start();
}
}
public static int mapColor(@GCodeViewer.ExtrusionRole int index) {
switch (index) {
default:
case GCodeViewer.EXTRUSION_ROLE_NONE:
return ThemesRepo.getColor(R.attr.gcodeViewerNone);
case GCodeViewer.EXTRUSION_ROLE_PERIMETER:
return ThemesRepo.getColor(R.attr.gcodeViewerPerimeter);
case GCodeViewer.EXTRUSION_ROLE_EXTERNAL_PERIMETER:
return ThemesRepo.getColor(R.attr.gcodeViewerExternalPerimeter);
case GCodeViewer.EXTRUSION_ROLE_OVERHANG_PERIMETER:
return ThemesRepo.getColor(R.attr.gcodeViewerOverhangPerimeter);
case GCodeViewer.EXTRUSION_ROLE_INTERNAL_INFILL:
return ThemesRepo.getColor(R.attr.gcodeViewerInternalInfill);
case GCodeViewer.EXTRUSION_ROLE_SOLID_INFILL:
return ThemesRepo.getColor(R.attr.gcodeViewerSolidInfill);
case GCodeViewer.EXTRUSION_ROLE_TOP_SOLID_INFILL:
return ThemesRepo.getColor(R.attr.gcodeViewerTopSolidInfill);
case GCodeViewer.EXTRUSION_ROLE_IRONING:
return ThemesRepo.getColor(R.attr.gcodeViewerIroning);
case GCodeViewer.EXTRUSION_ROLE_BRIDGE_INFILL:
return ThemesRepo.getColor(R.attr.gcodeViewerBridgeInfill);
case GCodeViewer.EXTRUSION_ROLE_GAP_FILL:
return ThemesRepo.getColor(R.attr.gcodeViewerGapFill);
case GCodeViewer.EXTRUSION_ROLE_SKIRT:
return ThemesRepo.getColor(R.attr.gcodeViewerSkirt);
case GCodeViewer.EXTRUSION_ROLE_SUPPORT_MATERIAL:
return ThemesRepo.getColor(R.attr.gcodeViewerSupportMaterial);
case GCodeViewer.EXTRUSION_ROLE_SUPPORT_MATERIAL_INTERFACE:
return ThemesRepo.getColor(R.attr.gcodeViewerSupportMaterialInterface);
case GCodeViewer.EXTRUSION_ROLE_WIPE_TOWER:
return ThemesRepo.getColor(R.attr.gcodeViewerWipeTower);
case GCodeViewer.EXTRUSION_ROLE_CUSTOM:
return ThemesRepo.getColor(R.attr.gcodeViewerCustom);
}
}
@Override
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
canvas.save();
path.rewind();
float cX = getWidth() / 2f;
float l = 0, r = ViewUtils.lerp(0, getWidth(), showProgress);
path.addRoundRect(l, 0, r, getHeight(), ViewUtils.dp(12), ViewUtils.dp(12), Path.Direction.CW);
canvas.clipPath(path);
if (currentValues != null) {
float dw = r - l;
for (int i = 1; i < currentValues.length; i++) {
float prev = currentValues[i - 1];
float to = currentValues[i];
paint.setColor(mapColor(i - 1));
canvas.drawRect(l + prev * dw, 0, l + to * dw, getHeight(), paint);
}
}
canvas.restore();
}
}
@@ -1139,4 +1139,77 @@ extern "C" {
ref->viewer.shutdown();
delete ref;
}
JNIEXPORT jfloat JNICALL Java_ru_ytkab0bp_slicebeam_slic3r_Native_vgcode_1get_1estimated_1time(JNIEnv* env, jclass, jlong ptr) {
GCodeViewerRef* ref = (GCodeViewerRef*) (intptr_t) ptr;
return ref->viewer.get_estimated_time();
}
libvgcode::EGCodeExtrusionRole mapRole(int index) {
libvgcode::EGCodeExtrusionRole crole;
switch (index) {
default:
case 0:
crole = libvgcode::EGCodeExtrusionRole::None;
break;
case 1:
crole = libvgcode::EGCodeExtrusionRole::Perimeter;
break;
case 2:
crole = libvgcode::EGCodeExtrusionRole::ExternalPerimeter;
break;
case 3:
crole = libvgcode::EGCodeExtrusionRole::OverhangPerimeter;
break;
case 4:
crole = libvgcode::EGCodeExtrusionRole::InternalInfill;
break;
case 5:
crole = libvgcode::EGCodeExtrusionRole::SolidInfill;
break;
case 6:
crole = libvgcode::EGCodeExtrusionRole::TopSolidInfill;
break;
case 7:
crole = libvgcode::EGCodeExtrusionRole::Ironing;
break;
case 8:
crole = libvgcode::EGCodeExtrusionRole::BridgeInfill;
break;
case 9:
crole = libvgcode::EGCodeExtrusionRole::GapFill;
break;
case 10:
crole = libvgcode::EGCodeExtrusionRole::Skirt;
break;
case 11:
crole = libvgcode::EGCodeExtrusionRole::SupportMaterial;
break;
case 12:
crole = libvgcode::EGCodeExtrusionRole::SupportMaterialInterface;
break;
case 13:
crole = libvgcode::EGCodeExtrusionRole::WipeTower;
break;
case 14:
crole = libvgcode::EGCodeExtrusionRole::Custom;
break;
}
return crole;
}
JNIEXPORT jfloat JNICALL Java_ru_ytkab0bp_slicebeam_slic3r_Native_vgcode_1get_1estimated_1time_1role(JNIEnv* env, jclass, jlong ptr, jint role) {
GCodeViewerRef* ref = (GCodeViewerRef*) (intptr_t) ptr;
return ref->viewer.get_extrusion_role_estimated_time(mapRole(role));
}
JNIEXPORT jboolean JNICALL Java_ru_ytkab0bp_slicebeam_slic3r_Native_vgcode_1is_1extrusion_1role_1visible(JNIEnv* env, jclass, jlong ptr, jint role) {
GCodeViewerRef* ref = (GCodeViewerRef*) (intptr_t) ptr;
return ref->viewer.is_extrusion_role_visible(mapRole(role));
}
JNIEXPORT void JNICALL Java_ru_ytkab0bp_slicebeam_slic3r_Native_vgcode_1toggle_1extrusion_1role_1visibility(JNIEnv* env, jclass, jlong ptr, jint role) {
GCodeViewerRef* ref = (GCodeViewerRef*) (intptr_t) ptr;
ref->viewer.toggle_extrusion_role_visibility(mapRole(role));
}
}
+5 -2
View File
@@ -68,8 +68,11 @@
<string name="MenuTransformMirrorZ">Отразить по Z</string>
<string name="MenuModifiers">Модификаторы</string>
<string name="MenuSlice">Нарезка</string>
<!-- <string name="MenuSliceInfo">Информ.\nо печати</string>-->
<string name="MenuSliceInfo">Слои</string>
<string name="MenuSliceInfo">Информ.\nо печати</string>
<string name="MenuSliceInfoSecond">с.</string>
<string name="MenuSliceInfoMinute">мин.</string>
<string name="MenuSliceInfoHour">ч.</string>
<string name="MenuSliceLayers">Слои</string>
<string name="MenuSliceInfoLayers">Слои %d - %d</string>
<string name="MenuSliceExportToFile">Экспорт. в файл</string>
<string name="MenuSliceShare">Отправка</string>
+11 -3
View File
@@ -9,13 +9,21 @@
<attr name="dividerColor" format="reference|color"/>
<attr name="dividerContrastColor" format="reference|color"/>
<attr name="dialogBackground" format="reference|color"/>
<attr name="gcodeViewerSkirt" format="reference|color"/>
<attr name="gcodeViewerNone" format="reference|color"/>
<attr name="gcodeViewerPerimeter" format="reference|color"/>
<attr name="gcodeViewerExternalPerimeter" format="reference|color"/>
<attr name="gcodeViewerSupportMaterial" format="reference|color"/>
<attr name="gcodeViewerSupportMaterialInterface" format="reference|color"/>
<attr name="gcodeViewerOverhangPerimeter" format="reference|color"/>
<attr name="gcodeViewerInternalInfill" format="reference|color"/>
<attr name="gcodeViewerSolidInfill" format="reference|color"/>
<attr name="gcodeViewerTopSolidInfill" format="reference|color"/>
<attr name="gcodeViewerIroning" format="reference|color"/>
<attr name="gcodeViewerBridgeInfill" format="reference|color"/>
<attr name="gcodeViewerGapFill" format="reference|color"/>
<attr name="gcodeViewerSkirt" format="reference|color"/>
<attr name="gcodeViewerSupportMaterial" format="reference|color"/>
<attr name="gcodeViewerSupportMaterialInterface" format="reference|color"/>
<attr name="gcodeViewerWipeTower" format="reference|color"/>
<attr name="gcodeViewerCustom" format="reference|color"/>
<attr name="switchThumbUncheckedColor" format="reference|color"/>
<attr name="boostyColorTop" format="reference|color"/>
<attr name="boostyColorBottom" format="reference|color"/>
+5 -2
View File
@@ -69,8 +69,11 @@
<string name="MenuTransformMirrorZ">Mirror Z</string>
<string name="MenuModifiers">Modifiers</string>
<string name="MenuSlice">Slice</string>
<!-- <string name="MenuSliceInfo">Print\ninfo</string>-->
<string name="MenuSliceInfo">Layers</string>
<string name="MenuSliceInfo">Print\ninfo</string>
<string name="MenuSliceInfoSecond">s.</string>
<string name="MenuSliceInfoMinute">min.</string>
<string name="MenuSliceInfoHour">h.</string>
<string name="MenuSliceLayers">Layers</string>
<string name="MenuSliceInfoLayers">Layers %d - %d</string>
<string name="MenuSliceExportToFile">Export to file</string>
<string name="MenuSliceShare">Share</string>