diff --git a/app/src/main/java/ru/ytkab0bp/slicebeam/MainActivity.java b/app/src/main/java/ru/ytkab0bp/slicebeam/MainActivity.java
index ad86bd3..0ee0f3a 100644
--- a/app/src/main/java/ru/ytkab0bp/slicebeam/MainActivity.java
+++ b/app/src/main/java/ru/ytkab0bp/slicebeam/MainActivity.java
@@ -2,14 +2,20 @@ package ru.ytkab0bp.slicebeam;
import android.app.Activity;
import android.content.ContentResolver;
+import android.content.ContentValues;
import android.content.Intent;
import android.content.res.Configuration;
import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.Environment;
+import android.os.Process;
import android.provider.MediaStore;
+import android.util.Base64;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
@@ -28,6 +34,7 @@ import org.json.JSONArray;
import org.json.JSONObject;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -40,23 +47,27 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
+import java.util.UUID;
import java.util.zip.ZipFile;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.ChangeLogBottomSheet;
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
+import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import ru.ytkab0bp.slicebeam.navigation.MobileNavigationDelegate;
import ru.ytkab0bp.slicebeam.navigation.NavigationDelegate;
+import ru.ytkab0bp.slicebeam.slic3r.Model;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rRuntimeError;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.IOUtils;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
+import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
public class MainActivity extends AppCompatActivity {
public final static int REQUEST_CODE_OPEN_FILE = 1, REQUEST_CODE_EXPORT_GCODE = 2,
@@ -496,6 +507,51 @@ public class MainActivity extends AppCompatActivity {
}).start();
}
+ private void loadFile(File f, boolean autoorient) {
+ String tag = UUID.randomUUID().toString();
+ SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileOpenFileLoading).tag(tag));
+ IOUtils.IO_POOL.submit(() -> {
+ Process.setThreadPriority(-20);
+ if (delegate.getCurrentFragment() instanceof BedFragment) {
+ BedFragment fragment = (BedFragment) delegate.getCurrentFragment();
+ try {
+ boolean gcode = f.getName().endsWith(".gcode");
+ if (gcode) {
+ fragment.loadGCode(f);
+ } else {
+ fragment.loadModel(f);
+ }
+ fragment.getGlView().queueEvent(() -> {
+ if (!gcode) {
+ SliceBeam.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
+ }
+ Model model = fragment.getGlView().getRenderer().getModel();
+ int i = model.getObjectsCount() - 1;
+ if (autoorient) {
+ model.autoOrient(i);
+ fragment.getGlView().getRenderer().invalidateGlModel(i);
+ fragment.getGlView().requestRender();
+ }
+ SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
+ SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileOpenFileLoaded));
+ if (model.isBigObject(i)) {
+ SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.MenuFileOpenFileBigObject));
+ }
+ });
+ } catch (Slic3rRuntimeError e) {
+ Log.e("MainActivity", "Failed to load model", e);
+ f.delete();
+
+ ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
+ .setTitle(R.string.MenuFileOpenFileFailed)
+ .setMessage(e.toString())
+ .setPositiveButton(android.R.string.ok, null)
+ .show());
+ }
+ }
+ });
+ }
+
private void loadFile(Uri uri) {
if (uri == null) return;
@@ -534,30 +590,7 @@ public class MainActivity extends AppCompatActivity {
}
fos.close();
in.close();
-
- ViewUtils.postOnMainThread(() -> {
- if (delegate.getCurrentFragment() instanceof BedFragment) {
- BedFragment fragment = (BedFragment) delegate.getCurrentFragment();
- try {
- if (f.getName().endsWith(".gcode")) {
- fragment.loadGCode(f);
- } else {
- fragment.loadModel(f);
- SliceBeam.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
- }
- SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileOpenFileLoaded));
- } catch (Slic3rRuntimeError e) {
- Log.e("MainActivity", "Failed to load model", e);
- f.delete();
-
- ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
- .setTitle(R.string.MenuFileOpenFileFailed)
- .setMessage(e.toString())
- .setPositiveButton(android.R.string.ok, null)
- .show());
- }
- }
- });
+ loadFile(f, false);
} catch (Exception e) {
Log.e("MainActivity", "Failed to write cache file", e);
diff --git a/app/src/main/java/ru/ytkab0bp/slicebeam/events/NeedDismissSnackbarEvent.java b/app/src/main/java/ru/ytkab0bp/slicebeam/events/NeedDismissSnackbarEvent.java
new file mode 100644
index 0000000..eb04dbb
--- /dev/null
+++ b/app/src/main/java/ru/ytkab0bp/slicebeam/events/NeedDismissSnackbarEvent.java
@@ -0,0 +1,12 @@
+package ru.ytkab0bp.slicebeam.events;
+
+import ru.ytkab0bp.eventbus.Event;
+
+@Event
+public class NeedDismissSnackbarEvent {
+ public final String tag;
+
+ public NeedDismissSnackbarEvent(String tag) {
+ this.tag = tag;
+ }
+}
diff --git a/app/src/main/java/ru/ytkab0bp/slicebeam/events/NeedSnackbarEvent.java b/app/src/main/java/ru/ytkab0bp/slicebeam/events/NeedSnackbarEvent.java
index 783a353..b10115b 100644
--- a/app/src/main/java/ru/ytkab0bp/slicebeam/events/NeedSnackbarEvent.java
+++ b/app/src/main/java/ru/ytkab0bp/slicebeam/events/NeedSnackbarEvent.java
@@ -2,10 +2,18 @@ package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event;
import ru.ytkab0bp.slicebeam.SliceBeam;
+import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
@Event
public class NeedSnackbarEvent {
public final CharSequence title;
+ public SnackbarsLayout.Type type = SnackbarsLayout.Type.DONE;
+ public String tag;
+
+ public NeedSnackbarEvent(SnackbarsLayout.Type type, CharSequence title) {
+ this.type = type;
+ this.title = title;
+ }
public NeedSnackbarEvent(CharSequence title) {
this.title = title;
@@ -14,4 +22,14 @@ public class NeedSnackbarEvent {
public NeedSnackbarEvent(int title, Object... args) {
this.title = SliceBeam.INSTANCE.getString(title, args);
}
+
+ public NeedSnackbarEvent(SnackbarsLayout.Type type, int title, Object... args) {
+ this.type = type;
+ this.title = SliceBeam.INSTANCE.getString(title, args);
+ }
+
+ public NeedSnackbarEvent tag(String tag) {
+ this.tag = tag;
+ return this;
+ }
}
diff --git a/app/src/main/java/ru/ytkab0bp/slicebeam/fragment/BedFragment.java b/app/src/main/java/ru/ytkab0bp/slicebeam/fragment/BedFragment.java
index 74a1071..c5c5b76 100644
--- a/app/src/main/java/ru/ytkab0bp/slicebeam/fragment/BedFragment.java
+++ b/app/src/main/java/ru/ytkab0bp/slicebeam/fragment/BedFragment.java
@@ -14,13 +14,11 @@ import android.webkit.WebView;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
-import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.dynamicanimation.animation.FloatValueHolder;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import com.google.android.material.navigation.NavigationBarView;
-import com.google.android.material.snackbar.Snackbar;
import java.io.File;
@@ -38,6 +36,7 @@ import ru.ytkab0bp.slicebeam.components.bed_menu.SliceMenu;
import ru.ytkab0bp.slicebeam.components.bed_menu.TransformMenu;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.events.FlattenModeResetEvent;
+import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.SlicingProgressEvent;
import ru.ytkab0bp.slicebeam.navigation.Fragment;
@@ -51,6 +50,7 @@ import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.BedSwipeDownLayout;
import ru.ytkab0bp.slicebeam.view.DividerView;
import ru.ytkab0bp.slicebeam.view.GLView;
+import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
import ru.ytkab0bp.slicebeam.view.ThemeBottomNavigationView;
import ru.ytkab0bp.slicebeam.view.ThemeRailNavigationView;
@@ -59,7 +59,7 @@ public class BedFragment extends Fragment {
private final static int MENU_SIZE_DP = 80;
private FrameLayout overlayLayout;
- private CoordinatorLayout snackbarsLayout;
+ private SnackbarsLayout snackbarsLayout;
private GLView glView;
private NavigationBarView navigationView;
@@ -126,7 +126,16 @@ public class BedFragment extends Fragment {
@EventHandler(runOnMainThread = true)
public void onNeedSnackbar(NeedSnackbarEvent e) {
- Snackbar.make(snackbarsLayout, e.title, Snackbar.LENGTH_SHORT).show();
+ SnackbarsLayout.Snackbar s = new SnackbarsLayout.Snackbar(e.type, e.title);
+ if (e.tag != null) {
+ s.tag(e.tag);
+ }
+ snackbarsLayout.show(s);
+ }
+
+ @EventHandler(runOnMainThread = true)
+ public void onDismissSnackbar(NeedDismissSnackbarEvent e) {
+ snackbarsLayout.dismiss(e.tag);
}
public void showUnfoldMenu(UnfoldMenu menu, View from) {
@@ -380,7 +389,7 @@ public class BedFragment extends Fragment {
} else {
overlayLayout.addView(contentView = ll);
}
- overlayLayout.addView(snackbarsLayout = new CoordinatorLayout(ctx), new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) {{
+ overlayLayout.addView(snackbarsLayout = new SnackbarsLayout(ctx), new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) {{
if (portrait) {
bottomMargin = ViewUtils.dp(80 * 2);
} else {
@@ -390,7 +399,7 @@ public class BedFragment extends Fragment {
return overlayLayout;
}
- public CoordinatorLayout getSnackbarsLayout() {
+ public SnackbarsLayout getSnackbarsLayout() {
return snackbarsLayout;
}
@@ -498,7 +507,6 @@ public class BedFragment extends Fragment {
}
});
} else {
- glView.getRenderer().setModel(model = m);
glView.queueEvent(new Runnable() {
@Override
public void run() {
@@ -507,6 +515,8 @@ public class BedFragment extends Fragment {
ViewUtils.postOnMainThread(()-> glView.queueEvent(this));
return;
}
+ glView.getRenderer().setModel(model = m);
+
Vec3d center = bed.getVolumeMin().center(bed.getVolumeMax());
Vec3d objMin = new Vec3d(), objMax = new Vec3d();
Vec3d objTranslate = new Vec3d();
diff --git a/app/src/main/java/ru/ytkab0bp/slicebeam/slic3r/Model.java b/app/src/main/java/ru/ytkab0bp/slicebeam/slic3r/Model.java
index 229ced6..51f9055 100644
--- a/app/src/main/java/ru/ytkab0bp/slicebeam/slic3r/Model.java
+++ b/app/src/main/java/ru/ytkab0bp/slicebeam/slic3r/Model.java
@@ -147,10 +147,22 @@ public class Model {
return list;
}
+ public int getExtruder(int i) {
+ return Native.model_get_extruder(pointer, i);
+ }
+
+ public void setExtruder(int i, int extruder) {
+ Native.model_set_extruder(pointer, i, extruder);
+ }
+
public void autoOrient(int i) {
Native.model_auto_orient(pointer, i);
}
+ public boolean isBigObject(int i) {
+ return Native.model_is_big_object(pointer, i);
+ }
+
public GCodeProcessorResult slice(String configPath, String gcodePath, SliceListener listener) throws Slic3rRuntimeError {
return new GCodeProcessorResult(Native.model_slice(pointer, configPath, gcodePath, listener));
}
diff --git a/app/src/main/java/ru/ytkab0bp/slicebeam/slic3r/Native.java b/app/src/main/java/ru/ytkab0bp/slicebeam/slic3r/Native.java
index 9a1751b..693cf0c 100644
--- a/app/src/main/java/ru/ytkab0bp/slicebeam/slic3r/Native.java
+++ b/app/src/main/java/ru/ytkab0bp/slicebeam/slic3r/Native.java
@@ -72,6 +72,9 @@ class Native {
static native void model_flatten_rotate(long ptr, int i, long surfacePtr);
static native long[] model_create_flatten_planes(long ptr, int i);
static native void model_auto_orient(long ptr, int i);
+ static native boolean model_is_big_object(long ptr, int i);
+ static native int model_get_extruder(long ptr, int i);
+ static native void model_set_extruder(long ptr, int i, int extruder);
static native long model_slice(long ptr, String configPath, String path, SliceListener listener) throws Slic3rRuntimeError;
static native void model_release(long ptr);
diff --git a/app/src/main/java/ru/ytkab0bp/slicebeam/theme/BeamTheme.java b/app/src/main/java/ru/ytkab0bp/slicebeam/theme/BeamTheme.java
index 72925b7..19d01e8 100644
--- a/app/src/main/java/ru/ytkab0bp/slicebeam/theme/BeamTheme.java
+++ b/app/src/main/java/ru/ytkab0bp/slicebeam/theme/BeamTheme.java
@@ -47,6 +47,12 @@ public class BeamTheme {
colors.put(R.attr.yTrackColor, 0xff00bf00);
colors.put(R.attr.zTrackColor, 0xff0000bf);
+ colors.put(R.attr.snackbarBase, 0xFFEEEEEE);
+ colors.put(R.attr.snackbarDone, 0xFF56AB2F);
+ colors.put(R.attr.snackbarWarning, 0xFFAE660C);
+ colors.put(R.attr.snackbarInfo, 0xFF009DC6);
+ colors.put(R.attr.snackbarError, 0xFFDC100E);
+
colors.put(android.R.attr.textColorPrimary, 0xff000000);
colors.put(android.R.attr.textColorSecondary, 0x99000000);
colors.put(android.R.attr.windowBackground, 0xffffffff);
@@ -73,6 +79,8 @@ public class BeamTheme {
colors.put(R.attr.yTrackColor, 0xff00ee00);
colors.put(R.attr.zTrackColor, 0xff0000ee);
+ colors.put(R.attr.snackbarBase, 0xFF212121);
+
colors.put(android.R.attr.textColorPrimary, 0xffffffff);
colors.put(android.R.attr.textColorSecondary, 0x99ffffff);
colors.put(android.R.attr.windowBackground, 0xff121212);
diff --git a/app/src/main/java/ru/ytkab0bp/slicebeam/utils/IOUtils.java b/app/src/main/java/ru/ytkab0bp/slicebeam/utils/IOUtils.java
index c65588f..efe9cda 100644
--- a/app/src/main/java/ru/ytkab0bp/slicebeam/utils/IOUtils.java
+++ b/app/src/main/java/ru/ytkab0bp/slicebeam/utils/IOUtils.java
@@ -15,10 +15,15 @@ import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
public class IOUtils {
+ public static ExecutorService IO_POOL = Executors.newCachedThreadPool();
+
public static String readString(InputStream in) throws IOException {
return readString(in, false);
}
diff --git a/app/src/main/java/ru/ytkab0bp/slicebeam/view/SnackbarsLayout.java b/app/src/main/java/ru/ytkab0bp/slicebeam/view/SnackbarsLayout.java
new file mode 100644
index 0000000..a9b01be
--- /dev/null
+++ b/app/src/main/java/ru/ytkab0bp/slicebeam/view/SnackbarsLayout.java
@@ -0,0 +1,307 @@
+package ru.ytkab0bp.slicebeam.view;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Outline;
+import android.text.TextUtils;
+import android.util.TypedValue;
+import android.view.GestureDetector;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.graphics.ColorUtils;
+import androidx.dynamicanimation.animation.FloatValueHolder;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
+import java.util.Objects;
+
+import ru.ytkab0bp.slicebeam.R;
+import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
+import ru.ytkab0bp.slicebeam.utils.ViewUtils;
+
+public class SnackbarsLayout extends FrameLayout {
+ public SnackbarsLayout(@NonNull Context context) {
+ super(context);
+ }
+
+ public void show(Snackbar snackbar) {
+ SnackbarView v = new SnackbarView(getContext()).bind(snackbar);
+ addView(v);
+ applyTransforms();
+ new SpringAnimation(new FloatValueHolder(0))
+ .setMinimumVisibleChange(1 / 500f)
+ .setSpring(new SpringForce(1f)
+ .setStiffness(1000f)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY))
+ .addUpdateListener((animation, value, velocity) -> {
+ v.progress = value;
+ applyTransforms();
+ })
+ .start();
+
+ if (snackbar.lifetime > 0) {
+ ViewUtils.postOnMainThread(v::dismiss, snackbar.lifetime);
+ }
+ }
+
+ public void dismiss(String tag) {
+ for (int i = 0, s = getChildCount(); i < s; i++) {
+ SnackbarView snackbar = (SnackbarView) getChildAt(i);
+ if (Objects.equals(snackbar.snackbar.tag, tag)) {
+ snackbar.dismiss();
+ }
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ applyTransforms();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ applyTransforms();
+ }
+
+ private void applyTransforms() {
+ float y = getHeight() - ViewUtils.dp(8);
+
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ SnackbarView snackbar = (SnackbarView) getChildAt(i);
+ if (snackbar.getTag() == null) {
+ snackbar.setAlpha(snackbar.progress);
+ }
+
+ float yOff = snackbar.getAlpha() * snackbar.progress * (snackbar.getHeight() + ViewUtils.dp(8));
+ y -= yOff;
+ snackbar.setTranslationY(y);
+ }
+ }
+
+ private class SnackbarView extends LinearLayout {
+ private final static int MARGIN_DP = 8;
+
+ private ProgressBar progressBar;
+ private ImageView icon;
+ private TextView title;
+ private Snackbar snackbar;
+
+ private float progress;
+
+ private GestureDetector gestureDetector;
+
+ SnackbarView(Context context) {
+ super(context);
+
+ setElevation(ViewUtils.dp(4));
+ setClipToOutline(true);
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(0, 0, getWidth(), getHeight(), ViewUtils.dp(16));
+ }
+ });
+ setAlpha(0f);
+ setPadding(ViewUtils.dp(10), ViewUtils.dp(10), ViewUtils.dp(10), ViewUtils.dp(10));
+ setMinimumHeight(ViewUtils.dp(48));
+
+ setOrientation(HORIZONTAL);
+ setGravity(Gravity.CENTER_VERTICAL);
+
+ FrameLayout fl = new FrameLayout(context);
+ icon = new ImageView(context);
+ fl.addView(icon, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ progressBar = new ProgressBar(context);
+ progressBar.setVisibility(GONE);
+ fl.addView(progressBar, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ addView(fl, new LinearLayout.LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28)) {{
+ setMarginStart(ViewUtils.dp(4));
+ setMarginEnd(ViewUtils.dp(14));
+ }});
+ title = new TextView(context);
+ title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
+ title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
+ title.setMaxLines(2);
+ title.setEllipsize(TextUtils.TruncateAt.END);
+ addView(title);
+
+ setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
+ leftMargin = topMargin = rightMargin = bottomMargin = ViewUtils.dp(MARGIN_DP);
+ }});
+
+ gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onDown(@NonNull MotionEvent e) {
+ return true;
+ }
+
+ @Override
+ public boolean onScroll(@Nullable MotionEvent e1, @NonNull MotionEvent e2, float distanceX, float distanceY) {
+ getParent().requestDisallowInterceptTouchEvent(true);
+
+ float off = e2.getX() - e1.getX();
+ setTranslationX(off);
+ return true;
+ }
+
+ @Override
+ public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY) {
+ if (snackbar.type == Type.LOADING) {
+ return false;
+ }
+
+ if (Math.abs(velocityX) >= 1500) {
+ if (velocityX > 0) {
+ animateTo(getWidth() + ViewUtils.dp(MARGIN_DP), true);
+ } else {
+ animateTo(-getWidth() - ViewUtils.dp(MARGIN_DP), true);
+ }
+ return true;
+ }
+
+ return false;
+ }
+ });
+ }
+
+ private void dismiss() {
+ setTag(1);
+
+ new SpringAnimation(new FloatValueHolder(0))
+ .setMinimumVisibleChange(1 / 500f)
+ .setSpring(new SpringForce(1f)
+ .setStiffness(1000f)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY))
+ .addUpdateListener((animation, value, velocity) -> {
+ setAlpha(1f - value);
+ applyTransforms();
+ })
+ .addEndListener((animation, canceled, value, velocity) -> {
+ if (getParent() == null) return;
+ ((ViewGroup) getParent()).removeView(this);
+ })
+ .start();
+ }
+
+ private void animateTo(float x, boolean remove) {
+ if (remove) {
+ setTag(1);
+ }
+ float start = getTranslationX();
+ new SpringAnimation(new FloatValueHolder(0))
+ .setMinimumVisibleChange(1 / 500f)
+ .setSpring(new SpringForce(1f)
+ .setStiffness(1000f)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY))
+ .addUpdateListener((animation, value, velocity) -> {
+ setTranslationX(ViewUtils.lerp(start, x, value));
+ if (remove) {
+ progress = 1f - value;
+ applyTransforms();
+ }
+ })
+ .addEndListener((animation, canceled, value, velocity) -> {
+ if (remove) {
+ ((ViewGroup) getParent()).removeView(SnackbarView.this);
+ }
+ })
+ .start();
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if ((event.getActionMasked() == MotionEvent.ACTION_UP || event.getActionMasked() == MotionEvent.ACTION_CANCEL) && getTag() == null) {
+ animateTo(0, false);
+ }
+
+ MotionEvent ev = MotionEvent.obtain(event);
+ ev.offsetLocation(getTranslationX(), 0);
+ boolean ret = gestureDetector.onTouchEvent(ev);
+ ev.recycle();
+ return ret;
+ }
+
+ SnackbarView bind(Snackbar snackbar) {
+ this.snackbar = snackbar;
+
+ progressBar.setVisibility(snackbar.type == Type.LOADING ? VISIBLE : GONE);
+ icon.setVisibility(snackbar.type == Type.LOADING ? GONE : VISIBLE);
+
+ title.setText(snackbar.title);
+ switch (snackbar.type) {
+ case DONE:
+ icon.setImageResource(R.drawable.done_outline_28);
+ icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.snackbarDone)));
+ title.setTextColor(ThemesRepo.getColor(R.attr.snackbarDone));
+ setBackgroundColor(ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.snackbarBase), ThemesRepo.getColor(R.attr.snackbarDone), 0.15f));
+ break;
+ case WARNING:
+ icon.setImageResource(R.drawable.warning_triangle_outline_28);
+ icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.snackbarWarning)));
+ title.setTextColor(ThemesRepo.getColor(R.attr.snackbarWarning));
+ setBackgroundColor(ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.snackbarBase), ThemesRepo.getColor(R.attr.snackbarWarning), 0.15f));
+ break;
+ case LOADING:
+ progressBar.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.snackbarInfo)));
+ title.setTextColor(ThemesRepo.getColor(R.attr.snackbarInfo));
+ setBackgroundColor(ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.snackbarBase), ThemesRepo.getColor(R.attr.snackbarInfo), 0.15f));
+ break;
+ case INFO:
+ icon.setImageResource(R.drawable.info_outline_28);
+ icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.snackbarInfo)));
+ title.setTextColor(ThemesRepo.getColor(R.attr.snackbarInfo));
+ setBackgroundColor(ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.snackbarBase), ThemesRepo.getColor(R.attr.snackbarInfo), 0.15f));
+ break;
+ case ERROR:
+ icon.setImageResource(R.drawable.error_outline_28);
+ icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.snackbarError)));
+ title.setTextColor(ThemesRepo.getColor(R.attr.snackbarError));
+ setBackgroundColor(ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.snackbarBase), ThemesRepo.getColor(R.attr.snackbarError), 0.15f));
+ break;
+ }
+ return this;
+ }
+ }
+
+ public static class Snackbar {
+ public CharSequence title;
+ public Type type;
+ public int lifetime = 2500;
+ public String tag;
+
+ public Snackbar(Type type, CharSequence title) {
+ this.type = type;
+ this.title = title;
+
+ if (type == Type.WARNING || type == Type.ERROR) {
+ lifetime = 5000;
+ }
+ }
+
+ public Snackbar tag(String tag) {
+ this.lifetime = 0;
+ this.tag = tag;
+ return this;
+ }
+ }
+
+ public enum Type {
+ DONE, WARNING, INFO, ERROR,
+ LOADING // Must use tag
+ }
+}
diff --git a/app/src/main/jni/slicebeam/beam_native.cpp b/app/src/main/jni/slicebeam/beam_native.cpp
index ab99764..a9699e5 100644
--- a/app/src/main/jni/slicebeam/beam_native.cpp
+++ b/app/src/main/jni/slicebeam/beam_native.cpp
@@ -817,6 +817,24 @@ extern "C" {
orientation::orient(obj);
}
+ JNIEXPORT jboolean JNICALL Java_ru_ytkab0bp_slicebeam_slic3r_Native_model_1is_1big_1object(JNIEnv* env, jclass, jlong ptr, jint i) {
+ ModelRef* model = (ModelRef*) (intptr_t) ptr;
+ ModelObject* obj = model->model.objects[i];
+ return obj->volumes.size() == 1 && obj->volumes.front()->mesh().its.indices.size() >= 100000;
+ }
+
+ JNIEXPORT jint JNICALL Java_ru_ytkab0bp_slicebeam_slic3r_Native_model_1get_1extruder(JNIEnv* env, jclass, jlong ptr, jint i) {
+ ModelRef* model = (ModelRef*) (intptr_t) ptr;
+ ModelObject* obj = model->model.objects[i];
+ return obj->config.has("extruder") ? obj->config.opt_int("extruder") : -1;
+ }
+
+ JNIEXPORT void JNICALL Java_ru_ytkab0bp_slicebeam_slic3r_Native_model_1set_1extruder(JNIEnv* env, jclass, jlong ptr, jint i, jint extruder) {
+ ModelRef* model = (ModelRef*) (intptr_t) ptr;
+ ModelObject* obj = model->model.objects[i];
+ obj->config.set("extruder", extruder);
+ }
+
JNIEXPORT jlong JNICALL Java_ru_ytkab0bp_slicebeam_slic3r_Native_model_1slice(JNIEnv* env, jclass, jlong ptr, jstring configPath, jstring path, jobject listener) {
try {
ModelRef* model = (ModelRef*) (intptr_t) ptr;
@@ -1453,6 +1471,7 @@ extern "C" {
ref->data = libvgcode_convert_input_data(resultRef->result, resultRef->result.extruder_colors, resultRef->result.extruder_colors, ref->viewer);
ref->viewer.load(std::move(ref->data));
+ ref->viewer.set_time_mode(libvgcode::ETimeMode::Normal);
}
JNIEXPORT void JNICALL Java_ru_ytkab0bp_slicebeam_slic3r_Native_vgcode_1reset(JNIEnv* env, jclass, jlong ptr) {
diff --git a/app/src/main/res/drawable/done_outline_28.xml b/app/src/main/res/drawable/done_outline_28.xml
new file mode 100644
index 0000000..fc9471c
--- /dev/null
+++ b/app/src/main/res/drawable/done_outline_28.xml
@@ -0,0 +1,12 @@
+
+
+
diff --git a/app/src/main/res/drawable/error_outline_28.xml b/app/src/main/res/drawable/error_outline_28.xml
new file mode 100644
index 0000000..7db20a2
--- /dev/null
+++ b/app/src/main/res/drawable/error_outline_28.xml
@@ -0,0 +1,12 @@
+
+
+
diff --git a/app/src/main/res/drawable/warning_triangle_outline_28.xml b/app/src/main/res/drawable/warning_triangle_outline_28.xml
new file mode 100644
index 0000000..2f3aac1
--- /dev/null
+++ b/app/src/main/res/drawable/warning_triangle_outline_28.xml
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 0225efc..297d341 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -10,8 +10,11 @@
Настройки приложения
Файл
Открыть модель
+ Файл загружен.
Не удалось открыть модель
Не удалось определить имя файла.
+ Файл содержит более 100к треугольников. Нарезка может быть очень медленной.
+ Загрузка файла…
Убрать модель
Калибров.
K3D Linear Advance
@@ -28,7 +31,6 @@
Цилиндр
Пирамида
Сфера
- Файл загружен.
Импорт. профилей
Не удалось импортировать
Не файл .ini
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 9aef6b9..e6a533a 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -34,4 +34,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6e101a5..ce2d443 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -14,6 +14,8 @@
File loaded.
Failed to open model
Failed to resolve file name.
+ File has more than 100k triangles. Processing could be really slow.
+ Loading file…
Remove model
Calibrat.
K3D Linear Advance
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 9a923e5..012179d 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,5 +1,4 @@
-