Public source code release

This commit is contained in:
YTKAB0BP
2024-11-01 08:28:55 +03:00
parent 20b730b1c8
commit 0b2ba24c7f
6691 changed files with 2325292 additions and 1 deletions
@@ -0,0 +1,166 @@
package ru.ytkab0bp.slicebeam.slic3r;
import static android.opengl.GLES30.*;
import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
import java.io.File;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.render.CoordAxes;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
import ru.ytkab0bp.slicebeam.utils.Vec3d;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public class Bed3D {
private final static float GROUND_Z = -0.02f;
private long pointer;
private GLModel triangles;
private GLModel gridlines;
private GLModel contourlines;
private CoordAxes axes = new CoordAxes();
private double[] boundingVolume;
private Vec3d min, max;
private boolean likelyDelta;
private double[] modelMatrix = new double[16];
private double[] outModelMatrix = new double[16];
public Bed3D() {
long[] data = new long[3];
pointer = Native.bed_create(data);
triangles = new GLModel(data[0]);
gridlines = new GLModel(data[1]);
contourlines = new GLModel(data[2]);
}
public void configure(File f) {
configure(f.getAbsolutePath());
}
private void configure(String path) {
Native.bed_configure(pointer, path);
boundingVolume = Native.bed_get_bounding_volume(pointer);
min = max = null;
axes.origin.set(0, 0, GROUND_Z);
axes.setStemLength(0.1f * Native.bed_get_bounding_volume_max_size(pointer));
if (isValid()) {
Vec3d center = getVolumeMin().center(getVolumeMax());
likelyDelta = (center.x == 0 || center.y == 0) && getVolumeMin().x < 0 && getVolumeMin().y < 0;
} else {
likelyDelta = false;
}
}
public void arrange(Model model) {
Native.bed_arrange(pointer, model.pointer);
}
public Vec3d getVolumeMin() {
if (min == null && boundingVolume != null) min = new Vec3d(boundingVolume[0], boundingVolume[1], boundingVolume[2]);
return min;
}
public Vec3d getVolumeMax() {
if (max == null && boundingVolume != null) max = new Vec3d(boundingVolume[3], boundingVolume[4], boundingVolume[5]);
return max;
}
public boolean isValid() {
return boundingVolume != null;
}
public void render(boolean bottom, double[] viewModelMatrix, double[] projectionMatrix, float invZoom) {
assertTrue(viewModelMatrix.length == 16);
assertTrue(projectionMatrix.length == 16);
DoubleMatrix.setIdentityM(modelMatrix, 0);
if (!likelyDelta) {
DoubleMatrix.translateM(modelMatrix, 0, -getVolumeMin().x * 2, -getVolumeMin().y * 2, -getVolumeMin().z);
}
DoubleMatrix.multiplyMM(outModelMatrix, 0, viewModelMatrix, 0, modelMatrix, 0);
renderDefaultBed(bottom, outModelMatrix, projectionMatrix);
axes.render(viewModelMatrix, projectionMatrix, 0.25f, invZoom);
}
private void renderDefaultBed(boolean bottom, double[] viewModelMatrix, double[] projectionMatrix) {
GLShaderProgram shader = GLShadersManager.get(GLShadersManager.SHADER_FLAT);
shader.startUsing();
shader.setUniformMatrix4fv("view_model_matrix", viewModelMatrix);
shader.setUniformMatrix4fv("projection_matrix", projectionMatrix);
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (!bottom) {
glDepthMask(false);
triangles.setColor(ThemesRepo.getColor(R.attr.defaultBedColor));
triangles.render();
glDepthMask(true);
}
glLineWidth(ViewUtils.dp(1));
gridlines.setColor(ThemesRepo.getColor(R.attr.bedGridlinesColor));
gridlines.render();
contourlines.setColor(ThemesRepo.getColor(R.attr.bedContourlinesColor));
contourlines.render();
glDisable(GL_BLEND);
shader.stopUsing();
}
private void renderTexturedBed(boolean bottom, float[] viewModelMatrix, float[] projectionMatrix) {
GLShaderProgram shader = GLShadersManager.get(GLShadersManager.SHADER_PRINTBED);
shader.startUsing();
shader.setUniform3f("view_model_matrix", viewModelMatrix);
shader.setUniform3f("projection_matrix", projectionMatrix);
shader.setUniform("transparent_background", bottom);
shader.setUniform("svg_source", false);
glEnable(GL_DEPTH_TEST);
if (bottom) {
glDepthMask(false);
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (bottom) {
glFrontFace(GL_CW);
}
// TODO: glBindTexture(GL_TEXTURE_2D, tex_id);
glBindTexture(GL_TEXTURE_2D, 0);
if (bottom) {
glFrontFace(GL_CCW);
}
glDisable(GL_BLEND);
if (bottom) {
glDepthMask(true);
}
shader.stopUsing();
}
public void release() {
Native.bed_release(pointer);
axes.release();
// triangles.release();
// gridlines.release();
// contourlines.release();
}
}
@@ -0,0 +1,163 @@
package ru.ytkab0bp.slicebeam.slic3r;
import android.text.TextUtils;
import androidx.annotation.Keep;
@Keep
public class ConfigOptionDef {
public String key;
// What type? bool, int, string etc.
public ConfigOptionType type = ConfigOptionType.NONE;
// Usually empty.
// Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection,
// "select_open" - to open a selection dialog (currently only a serial port selection).
public GUIType guiType;
// Label of the GUI input field.
// In case the GUI input fields are grouped in some views, the label defines a short label of a grouped value,
// while full_label contains a label of a stand-alone field.
// The full label is shown, when adding an override parameter for an object or a modified object.
public String label;
public String fullLabel;
// With which printer technology is this configuration valid?
public PrinterTechnology printerTechnology = PrinterTechnology.UNKNOWN;
// Category of a configuration field, from the GUI perspective.
// One of: "Layers and Perimeters", "Infill", "Support material", "Speed", "Extruders", "Advanced", "Extrusion Width"
public String category;
// A tooltip text shown in the GUI.
public String tooltip;
// Text right from the input field, usually a unit of measurement.
public String sidetext;
// True for multiline strings.
public boolean multiline;
// For text input: If true, the GUI text box spans the complete page width.
public boolean fullWidth;
// Not editable. Currently only used for the display of the number of threads.
public boolean readonly = false;
// Height of a multiline GUI text box.
public int height = -1;
// Optional width of an input field.
public int width = -1;
// <min, max> limit of a numeric input.
// If not set, the <min, max> is set to <INT_MIN, INT_MAX>
// By setting min=0, only nonnegative input is allowed.
public float min = Float.MIN_VALUE;
public float max = Float.MAX_VALUE;
public ConfigOptionMode mode = ConfigOptionMode.SIMPLE;
public String defaultValue;
public String[] enumLabels;
public String[] enumValues;
public String getLabel() {
return TextUtils.isEmpty(label) ? fullLabel : label;
}
public String getFullLabel() {
return TextUtils.isEmpty(fullLabel) ? label : fullLabel;
}
ConfigOptionDef() {}
public enum ConfigOptionType {
NONE,
// single float
FLOAT,
// vector of floats
FLOATS(true),
// single int
INT,
// vector of ints
INTS(true),
// single string
STRING,
// vector of strings
STRINGS(true),
// percent value. Currently only used for infill.
PERCENT,
// percents value. Currently used for retract before wipe only.
PERCENTS(true),
// a fraction or an absolute value
FLOAT_OR_PERCENT,
// vector of the above
FLOATS_OR_PERCENTS(true),
// single 2d point (Point2f). Currently not used.
POINT,
// vector of 2d points (Point2f). Currently used for the definition of the print bed and for the extruder offsets.
POINTS(true),
POINT3,
// single boolean value
BOOL,
// vector of boolean values
BOOLS(true),
// a generic enum
ENUM,
// vector of enum values
ENUMS;
public final boolean list;
ConfigOptionType() {
this(false);
}
ConfigOptionType(boolean list) {
this.list = list;
}
}
public enum GUIType {
UNDEFINED,
// Open enums, integer value could be one of the enumerated values or something else.
I_ENUM_OPEN,
// Open enums, float value could be one of the enumerated values or something else.
F_ENUM_OPEN,
// Open enums, string value could be one of the enumerated values or something else.
SELECT_OPEN,
// Color picker, string value.
COLOR,
// Currently unused.
SLIDER,
// Static text
LEGEND,
// Vector value, but edited as a single string.
ONE_STRING,
// Close parameter, string value could be one of the list values.
SELECT_CLOSE,
// Password, string vaule is hidden by asterisk.
PASSWORD
}
public enum PrinterTechnology {
// Fused Filament Fabrication
FFF,
// Stereolitography
SLA,
// Unknown, useful for command line processing
UNKNOWN,
// Any technology, useful for parameters compatible with both ptFFF and ptSLA
ANY
}
public enum ConfigOptionMode {
SIMPLE,
ADVANCED,
EXPERT,
UNDEFINED
}
}
@@ -0,0 +1,23 @@
package ru.ytkab0bp.slicebeam.slic3r;
import java.io.File;
public class GCodeProcessorResult {
final long pointer;
public GCodeProcessorResult(File f) {
pointer = Native.gcoderesult_load_file(f.getAbsolutePath(), f.getName());
}
GCodeProcessorResult(long ptr) {
pointer = ptr;
}
public String getRecommendedName() {
return Native.gcoderesult_get_recommended_name(pointer);
}
public void release() {
Native.gcoderesult_release(pointer);
}
}
@@ -0,0 +1,103 @@
package ru.ytkab0bp.slicebeam.slic3r;
import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
import android.graphics.Color;
import androidx.core.util.Pair;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
public class GCodeViewer {
private ThreadLocal<float[]> viewMatrixBuffer = new ThreadLocal<float[]>() {
@Override
protected float[] initialValue() {
return new float[16];
}
};
private ThreadLocal<float[]> projectionMatrixBuffer = new ThreadLocal<float[]>() {
@Override
protected float[] initialValue() {
return new float[16];
}
};
private final long pointer;
public GCodeViewer() {
pointer = Native.vgcode_create();
}
public boolean isInitialized() {
return Native.vgcode_is_initialized(pointer);
}
public void initGL() {
Native.vgcode_init(pointer);
}
public void load(GCodeProcessorResult result) {
Native.vgcode_load(pointer, result.pointer);
}
public void setLayersViewRange(long min, long max) {
Native.vgcode_set_layers_view_range(pointer, min, max);
}
public Pair<Long, Long> getLayersViewRange() {
long[] data = Native.vgcode_get_layers_view_range(pointer);
return new Pair<>(data[0], data[1] < 0 ? getLayersCount() : data[1]);
}
public long getLayersCount() {
return Native.vgcode_get_layers_count(pointer);
}
public void render(double[] viewMatrix, double[] projectionMatrix) {
assertTrue(viewMatrix.length == 16);
assertTrue(projectionMatrix.length == 16);
float[] vmFloats = viewMatrixBuffer.get();
for (int i = 0; i < viewMatrix.length; i++) {
vmFloats[i] = (float) viewMatrix[i];
}
float[] pmFloats = projectionMatrixBuffer.get();
for (int i = 0; i < projectionMatrix.length; i++) {
pmFloats[i] = (float) projectionMatrix[i];
}
Native.vgcode_render(pointer, vmFloats, pmFloats);
}
public void setThemeColors() {
setColors(
ThemesRepo.getColor(R.attr.gcodeViewerSkirt),
ThemesRepo.getColor(R.attr.gcodeViewerExternalPerimeter),
ThemesRepo.getColor(R.attr.gcodeViewerSupportMaterial),
ThemesRepo.getColor(R.attr.gcodeViewerSupportMaterialInterface),
ThemesRepo.getColor(R.attr.gcodeViewerInternalInfill),
ThemesRepo.getColor(R.attr.gcodeViewerSolidInfill),
ThemesRepo.getColor(R.attr.gcodeViewerWipeTower)
);
}
public void setColors(int skirt, int externalPerimeter, int supportMaterial, int supportMaterialInterface, int internalInfill, int solidInfill, int wipeTower) {
Native.vgcode_set_colors(pointer, new int[] {
Color.red(skirt), Color.green(skirt), Color.blue(skirt),
Color.red(externalPerimeter), Color.green(externalPerimeter), Color.blue(externalPerimeter),
Color.red(supportMaterial), Color.green(supportMaterial), Color.blue(supportMaterial),
Color.red(supportMaterialInterface), Color.green(supportMaterialInterface), Color.blue(supportMaterialInterface),
Color.red(internalInfill), Color.green(internalInfill), Color.blue(internalInfill),
Color.red(solidInfill), Color.green(solidInfill), Color.blue(solidInfill),
Color.red(wipeTower), Color.green(wipeTower), Color.blue(wipeTower)
});
}
public void reset() {
Native.vgcode_reset(pointer);
}
public void release() {
Native.vgcode_release(pointer);
}
}
@@ -0,0 +1,119 @@
package ru.ytkab0bp.slicebeam.slic3r;
import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
import android.graphics.Color;
import java.util.ArrayList;
import java.util.List;
import ru.ytkab0bp.slicebeam.render.Camera;
import ru.ytkab0bp.slicebeam.render.GLRenderer;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.Vec3d;
public class GLModel {
private long pointer;
private MeshRaycaster raycaster;
public float hoverProgress;
public boolean isHovering;
/* package */ GLModel(long ptr) {
pointer = ptr;
}
public GLModel() {
this(Native.glmodel_create());
}
public GLModel(Model model) {
this(Native.glmodel_create());
initFrom(model);
}
public void initFrom(Model model) {
Native.glmodel_init_from_model(pointer, model.pointer);
}
public void initFrom(Model model, int i) {
Native.glmodel_init_from_model_object(pointer, model.pointer, i);
}
public void setColor(int color) {
Native.glmodel_set_color(pointer, Color.red(color) / (float) 0xFF, Color.green(color) / (float) 0xFF, Color.blue(color) / (float) 0xFF, Color.alpha(color) / (float) 0xFF);
}
public void stilizedArrow(float tipRadius, float tipLength, float stemRadius, float stemLength) {
Native.glmodel_stilized_arrow(pointer, tipRadius, tipLength, stemRadius, stemLength);
}
public void initBackgroundTriangles() {
Native.glmodel_init_background_triangles(pointer);
}
public void initBoundingBox(Model model, int i) {
Native.glmodel_init_bounding_box(pointer, model.pointer, i);
}
public boolean isInitialized() {
return Native.glmodel_is_initialized(pointer);
}
public boolean isEmpty() {
return Native.glmodel_is_empty(pointer);
}
public void render() {
Native.glmodel_render(pointer);
}
public void reset() {
Native.glmodel_reset(pointer);
raycaster = null;
}
public void release() {
Native.glmodel_release(pointer);
}
public MeshRaycaster getRaycaster() {
if (raycaster == null) {
Native.glmodel_init_raycast_data(pointer);
raycaster = new MeshRaycaster();
}
return raycaster;
}
public final class MeshRaycaster {
public List<HitResult> raycast(GLRenderer renderer, ArrayList<HitResult> list, float x, float y) {
assertTrue(renderer != null);
list.clear();
Camera camera = renderer.getCamera();
Vec3d point = Slic3rUtils.unproject(camera.getViewModelMatrix(), renderer.getProjectionMatrix(), renderer.getViewportWidth(), renderer.getViewportHeight(), x, y);
Vec3d direction = camera.getDirForward().clone();
if (!Prefs.isOrthoProjectionEnabled()) {
direction = point.clone().add(camera.position.clone().negate()).normalize();
}
double[] v = Native.glmodel_raycast_closest_hit(pointer, point.asDoubleArray(), direction.asDoubleArray());
list.ensureCapacity(v.length / 6);
for (int i = 0; i < v.length; i += 6) {
list.add(new HitResult(
new Vec3d(v[i], v[i + 1], v[i + 2]),
new Vec3d(v[i + 3], v[i + 4], v[i + 5])
));
}
return list;
}
}
public static class HitResult {
public final Vec3d position, normal;
public HitResult(Vec3d position, Vec3d normal) {
this.position = position;
this.normal = normal;
}
}
}
@@ -0,0 +1,145 @@
package ru.ytkab0bp.slicebeam.slic3r;
import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
import android.content.res.AssetManager;
import android.graphics.Color;
import android.opengl.GLES30;
import androidx.annotation.Nullable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.utils.IOUtils;
public class GLShaderProgram {
final long pointer;
private static ThreadLocal<FloatBuffer> matrixBuffer = new ThreadLocal<FloatBuffer>() {
@Nullable
@Override
protected FloatBuffer initialValue() {
return ByteBuffer.allocateDirect(16 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
}
};
private static ThreadLocal<float[]> float16Buffer = new ThreadLocal<float[]>() {
@Override
protected float[] initialValue() {
return new float[16];
}
};
private static ThreadLocal<float[]> float12Buffer = new ThreadLocal<float[]>() {
@Override
protected float[] initialValue() {
return new float[12];
}
};
public GLShaderProgram(String name) {
AssetManager assets = SliceBeam.INSTANCE.getAssets();
try {
pointer = Native.shader_init_from_texts(name, IOUtils.readString(assets.open("shaders/" + name + ".fs")), IOUtils.readString(assets.open("shaders/" + name + ".vs")));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void startUsing() {
Native.shader_start_using(pointer);
}
public void stopUsing() {
Native.shader_stop_using(pointer);
}
public int getUniformLocation(String name) {
// This function uses native uniform cache. Java one does not
return Native.shader_get_uniform_location(pointer, name);
}
public int getAttribLocation(String name) {
// Same as getUniformLocation
return Native.shader_get_attrib_location(pointer, name);
}
public void setUniform(String name, boolean value) {
GLES30.glUniform1i(getUniformLocation(name), value ? 1 : 0);
}
public void setUniform(String name, float value) {
GLES30.glUniform1f(getUniformLocation(name), value);
}
public void setUniformMatrix3fv(String name, double[] value) {
assertTrue(value.length == 12);
float[] floats = float12Buffer.get();
for (int i = 0; i < value.length; i++) {
floats[i] = (float) value[i];
}
setUniformMatrix3fv(name, floats);
}
public void setUniformMatrix3fv(String name, float[] value) {
assertTrue(value.length == 12);
FloatBuffer buf = matrixBuffer.get();
buf.position(0).limit(12);
buf.put(value);
buf.flip();
GLES30.glUniformMatrix3fv(getUniformLocation(name), 1, false, buf);
}
public void setUniformMatrix4fv(String name, double[] value) {
assertTrue(value.length == 16);
float[] floats = float16Buffer.get();
for (int i = 0; i < value.length; i++) {
floats[i] = (float) value[i];
}
setUniformMatrix4fv(name, floats);
}
public void setUniformMatrix4fv(String name, float[] value) {
assertTrue(value.length == 16);
FloatBuffer buf = matrixBuffer.get();
buf.position(0).limit(16);
buf.put(value);
buf.flip();
GLES30.glUniformMatrix4fv(getUniformLocation(name), 1, false, buf);
}
public void setUniformColor(String name, int color) {
setUniform4f(name, (float) Color.red(color) / 0xFF, (float) Color.green(color) / 0xFF, (float) Color.blue(color) / 0xFF, (float) Color.alpha(color) / 0xFF);
}
public void setUniform4f(String name, float... value) {
assertTrue(value.length == 4);
GLES30.glUniform4f(getUniformLocation(name), value[0], value[1], value[2], value[3]);
}
public void setUniform3f(String name, float... value) {
assertTrue(value.length == 3);
GLES30.glUniform3f(getUniformLocation(name), value[0], value[1], value[2]);
}
public void setUniform2f(String name, float... value) {
assertTrue(value.length == 2);
GLES30.glUniform2f(getUniformLocation(name), value[0], value[1]);
}
public int getId() {
return Native.shader_get_id(pointer);
}
public void release() {
Native.shader_release(pointer);
}
}
@@ -0,0 +1,94 @@
package ru.ytkab0bp.slicebeam.slic3r;
import android.opengl.GLES30;
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
import androidx.annotation.StringDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Map;
public class GLShadersManager {
public final static String
SHADER_BACKGROUND = "background",
SHADER_DASHED_LINES = "dashed_lines",
SHADER_FLAT = "flat",
SHADER_FLAT_CLIP = "flat_clip",
SHADER_FLAT_TEXTURE = "flat_texture",
SHADER_GOURAUD = "gouraud",
SHADER_GOURAUD_LIGHT = "gouraud_light",
SHADER_GOURAUD_LIGHT_INSTANCED = "gouraud_light_instanced",
SHADER_IMGUI = "imgui",
SHADER_MM_CONTOUR = "mm_contour",
SHADER_MM_GOURAUD = "mm_gouraud",
SHADER_PRINTBED = "printbed",
SHADER_TOOLPATHS_COG = "toolpaths_cog",
SHADER_VARIABLE_LAYER_HEIGHT = "variable_layer_height",
SHADER_WIREFRAME = "wireframe",
SHADER_BEAM_INTRO = "beam_intro";
@StringDef(value = {
SHADER_BACKGROUND,
SHADER_DASHED_LINES,
SHADER_FLAT,
SHADER_FLAT_CLIP,
SHADER_FLAT_TEXTURE,
SHADER_GOURAUD,
SHADER_GOURAUD_LIGHT,
SHADER_GOURAUD_LIGHT_INSTANCED,
SHADER_IMGUI,
SHADER_MM_CONTOUR,
SHADER_MM_GOURAUD,
SHADER_GOURAUD,
SHADER_PRINTBED,
SHADER_TOOLPATHS_COG,
SHADER_VARIABLE_LAYER_HEIGHT,
SHADER_WIREFRAME,
SHADER_BEAM_INTRO
})
@Retention(RetentionPolicy.SOURCE)
public @interface ShaderType {}
private final static Map<String, GLShaderProgram> shaders = new HashMap<String, GLShaderProgram>() {
@Override
public GLShaderProgram get(@Nullable Object key) {
GLShaderProgram shader = super.get(key);
if (shader == null) put((String) key, shader = new GLShaderProgram((String) key));
return shader;
}
};
public static void clearShaders() {
for (GLShaderProgram program : shaders.values()) {
program.release();
}
shaders.clear();
}
public static GLShaderProgram get(@ShaderType String key) {
return shaders.get(key);
}
@Keep
private static long getCurrentShaderPointer() {
GLShaderProgram prog = getCurrentShader();
return prog != null ? prog.pointer : 0;
}
public static GLShaderProgram getCurrentShader() {
int[] idRef = {0};
GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, idRef, 0);
int id = idRef[0];
if (id != 0) {
for (GLShaderProgram program : shaders.values()) {
if (program.getId() == id) {
return program;
}
}
}
return null;
}
}
@@ -0,0 +1,147 @@
package ru.ytkab0bp.slicebeam.slic3r;
import java.io.File;
import java.util.UUID;
import ru.ytkab0bp.slicebeam.utils.Vec3d;
public class Model {
public final String key = UUID.randomUUID().toString();
final long pointer;
private double[] boundingExact;
private double[] boundingApprox;
public Model() {
this(Native.model_create());
}
public Model(File f) throws Slic3rRuntimeError {
this(f.getAbsolutePath());
}
public Model(String path) throws Slic3rRuntimeError {
this(Native.model_read_from_file(path, getBaseName(path)));
}
private Model(long ptr) {
this.pointer = ptr;
}
private static String getBaseName(String path) {
if (path.contains("/")) {
path = path.substring(path.lastIndexOf('/') + 1);
}
if (path.contains(".")) {
path = path.substring(0, path.lastIndexOf('.'));
}
return path;
}
public void getBoundingBoxExact(int i, Vec3d min, Vec3d max) {
double[] data = Native.model_get_bounding_box_exact(pointer, i);
min.set(data[0], data[1], data[2]);
max.set(data[3], data[4], data[5]);
}
public void getBoundingBoxApprox(int i, Vec3d min, Vec3d max) {
double[] data = Native.model_get_bounding_box_approx(pointer, i);
min.set(data[0], data[1], data[2]);
max.set(data[3], data[4], data[5]);
}
public Vec3d getBoundingBoxExactMin() {
if (boundingExact == null) boundingExact = Native.model_get_bounding_box_exact_global(pointer);
return new Vec3d(boundingExact[0], boundingExact[1], boundingExact[2]);
}
public Vec3d getBoundingBoxExactMax() {
if (boundingExact == null) boundingExact = Native.model_get_bounding_box_exact_global(pointer);
return new Vec3d(boundingExact[3], boundingExact[4], boundingExact[5]);
}
public Vec3d getBoundingBoxApproxMin() {
if (boundingApprox == null) boundingApprox = Native.model_get_bounding_box_approx_global(pointer);
return new Vec3d(boundingApprox[0], boundingApprox[1], boundingApprox[2]);
}
public Vec3d getBoundingBoxApproxMax() {
if (boundingApprox == null) boundingApprox = Native.model_get_bounding_box_approx_global(pointer);
return new Vec3d(boundingApprox[3], boundingApprox[4], boundingApprox[5]);
}
public void resetBoundingBox() {
boundingExact = null;
boundingApprox = null;
}
public void translate(int i, double x, double y, double z) {
Native.model_translate(pointer, i, x, y, z);
}
public void translate(double x, double y, double z) {
Native.model_translate_global(pointer, x, y, z);
resetBoundingBox();
}
public void scale(int i, double x, double y, double z) {
Native.model_scale(pointer, i, x, y, z);
}
public void rotate(int i, double x, double y, double z) {
Native.model_rotate(pointer, i, x, y, z);
}
public int getObjectsCount() {
return Native.model_get_objects_count(pointer);
}
public void addObject(Model from, int i) {
Native.model_add_object_from_another(pointer, from.pointer, i);
}
public void deleteObject(int i) {
Native.model_delete_object(pointer, i);
}
public void getTranslation(int i, Vec3d vec) {
double[] tr = Native.model_get_translation(pointer, i);
vec.set(tr[0], tr[1], tr[2]);
}
public void getRotation(int i, Vec3d vec) {
double[] tr = Native.model_get_rotation(pointer, i);
vec.set(tr[0], tr[1], tr[2]);
}
public boolean isLeftHanded(int i) {
return Native.model_is_left_handed(pointer, i);
}
public void getScale(int i, Vec3d vec) {
double[] tr = Native.model_get_scale(pointer, i);
vec.set(tr[0], tr[1], tr[2]);
}
public void getMirror(int i, Vec3d vec) {
double[] tr = Native.model_get_mirror(pointer, i);
vec.set(tr[0], tr[1], tr[2]);
}
public GCodeProcessorResult slice(String configPath, String gcodePath, SliceListener listener) throws Slic3rRuntimeError {
return new GCodeProcessorResult(Native.model_slice(pointer, configPath, gcodePath, listener));
}
public void release() {
Native.model_release(pointer);
}
public static Model merge(Model... models) {
long[] ptrs = new long[models.length];
for (int i = 0, modelsSize = models.length; i < modelsSize; i++) {
Model m = models[i];
ptrs[i] = m.pointer;
}
return new Model(Native.models_merge(ptrs));
}
}
@@ -0,0 +1,97 @@
package ru.ytkab0bp.slicebeam.slic3r;
import ru.ytkab0bp.slicebeam.SliceBeam;
class Native {
static {
System.loadLibrary("c++_shared");
System.loadLibrary("gmp");
System.loadLibrary("gmpxx");
System.loadLibrary("mpfr");
OCCTLoader.load();
System.loadLibrary("slic3r");
set_svg_path_prefix(SliceBeam.INSTANCE.getCacheDir().getAbsolutePath());
}
static native void get_print_config_def(PrintConfigDef def);
static native void set_svg_path_prefix(String prefix);
static native long shader_init_from_texts(String name, String fragment, String vertex);
static native int shader_get_id(long ptr);
static native int shader_get_uniform_location(long ptr, String name);
static native int shader_get_attrib_location(long ptr, String name);
static native void shader_start_using(long ptr);
static native void shader_stop_using(long ptr);
static native void shader_release(long ptr);
static native long glmodel_create();
static native void glmodel_init_from_model(long ptr, long model);
static native void glmodel_init_from_model_object(long ptr, long model, int i);
static native void glmodel_init_raycast_data(long ptr);
static native void glmodel_set_color(long ptr, float red, float green, float blue, float alpha);
static native void glmodel_render(long ptr);
static native void glmodel_stilized_arrow(long ptr, float tipRadius, float tipLength, float stemRadius, float stemLength);
static native void glmodel_init_background_triangles(long ptr);
static native void glmodel_init_bounding_box(long ptr, long modelPtr, int i);
static native boolean glmodel_is_initialized(long ptr);
static native boolean glmodel_is_empty(long ptr);
static native double[] glmodel_raycast_closest_hit(long ptr, double[] point, double[] direction);
static native void glmodel_reset(long ptr);
static native void glmodel_release(long ptr);
static native long bed_create(long[] data);
static native int bed_get_bounding_volume_max_size(long ptr);
static native double[] bed_get_bounding_volume(long ptr);
static native void bed_configure(long ptr, String configPath);
static native boolean bed_arrange(long ptr, long modelPtr);
static native void bed_release(long ptr);
static native long models_merge(long... ptrs);
static native long model_create();
static native long model_read_from_file(String path, String baseName) throws Slic3rRuntimeError;
static native int model_get_objects_count(long ptr);
static native void model_add_object_from_another(long ptr, long from, int i);
static native void model_delete_object(long ptr, int i);
static native double[] model_get_translation(long ptr, int objectIndex);
static native double[] model_get_scale(long ptr, int objectIndex);
static native double[] model_get_mirror(long ptr, int objectIndex);
static native double[] model_get_rotation(long ptr, int objectIndex);
static native double[] model_get_bounding_box_exact(long ptr, int i);
static native double[] model_get_bounding_box_approx(long ptr, int i);
static native double[] model_get_bounding_box_exact_global(long ptr);
static native double[] model_get_bounding_box_approx_global(long ptr);
static native boolean model_is_left_handed(long ptr, int i);
static native void model_translate(long ptr, int i, double x, double y, double z);
static native void model_translate_global(long ptr, double x, double y, double z);
static native void model_scale(long ptr, int i, double x, double y, double z);
static native void model_rotate(long ptr, int i, double x, double y, double z);
static native long model_slice(long ptr, String configPath, String path, SliceListener listener) throws Slic3rRuntimeError;
static native void model_release(long ptr);
static native long gcoderesult_load_file(String path, String name);
static native String gcoderesult_get_recommended_name(long ptr);
static native void gcoderesult_release(long ptr);
static native long vgcode_create();
static native void vgcode_init(long ptr);
static native boolean vgcode_is_initialized(long ptr);
static native void vgcode_set_colors(long ptr, int[] colors);
static native long vgcode_get_layers_count(long ptr);
static native void vgcode_load(long ptr, long resultPtr);
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 void vgcode_reset(long ptr);
static native void vgcode_release(long ptr);
static native long utils_config_create(String config);
static native boolean utils_config_check_compatibility(long ptr, String condition);
static native String utils_config_eval(long ptr, String condition) throws Slic3rRuntimeError;
static native void utils_config_release(long ptr);
static native void utils_calc_view_normal_matrix(double[] viewMatrix, double[] worldMatrix, double[] normalMatrix);
static native double[] utils_unproject(double[] viewMatrix, double[] projectionMatrix, int screenWidth, int screenHeight, double x, double y);
}
@@ -0,0 +1,36 @@
package ru.ytkab0bp.slicebeam.slic3r;
import java.util.Arrays;
import java.util.List;
class OCCTLoader {
private final static List<String> LIBS = Arrays.asList(
"TKDESTEP",
"TKXCAF",
"TKLCAF",
"TKCAF",
"TKCDF",
"TKV3d",
"TKMesh",
"TKXMesh",
"TKBO",
"TKPrim",
"TKHLR",
"TKShHealing",
"TKTopAlgo",
"TKGeomAlgo",
"TKGeomBase",
"TKBRep",
"TKG3d",
"TKG2d",
"TKMath",
"TKernel",
"TKDE"
);
static void load() {
for (String lib : LIBS) {
System.loadLibrary(lib);
}
}
}
@@ -0,0 +1,91 @@
package ru.ytkab0bp.slicebeam.slic3r;
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
import androidx.core.util.Pair;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PrintConfigDef {
public static List<String> SKIP_DEFAULT_OPTIONS = Arrays.asList(
"tilt_up_initial_speed",
"tilt_up_finish_speed",
"tilt_down_initial_speed",
"tilt_down_finish_speed",
"tower_speed"
);
private static PrintConfigDef instance;
private final static Map<String, Class<?>> clzMap = new HashMap<String, Class<?>>() {
@Nullable
@Override
public Class<?> get(@Nullable Object key) {
Class<?> clz = super.get(key);
if (clz == null) {
try {
put((String) key, clz = Class.forName((String) key));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
return clz;
}
};
private final static Map<Pair<Class<?>, String>, Field> fieldMap = new HashMap<Pair<Class<?>, String>, Field>() {
@Nullable
@Override
public Field get(@Nullable Object key) {
Field f = super.get(key);
if (f == null) {
Pair<Class<?>, String> k = (Pair<Class<?>, String>) key;
try {
f = k.first.getDeclaredField(k.second);
f.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
return f;
}
};
private final static Map<Pair<Class<?>, String>, Object> valueMap = new HashMap<>();
public Map<String, ConfigOptionDef> options = new HashMap<>();
@Keep
PrintConfigDef() {}
@Keep
static Object resolveEnum(String className, String value) {
className = className.replace("/", ".");
Class<?> clz = clzMap.get(className);
Pair<Class<?>, String> key = new Pair<>(clz, value);
Object val = valueMap.get(key);
if (val != null) return val;
Field f = fieldMap.get(key);
try {
valueMap.put(key, val = f.get(null));
return val;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static PrintConfigDef getInstance() {
if (instance == null) {
Native.get_print_config_def(instance = new PrintConfigDef());
}
return instance;
}
@Keep
void addOption(String key, ConfigOptionDef def) {
options.put(key, def);
}
}
@@ -0,0 +1,410 @@
package ru.ytkab0bp.slicebeam.slic3r;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import ru.ytkab0bp.slicebeam.BuildConfig;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
public class Slic3rConfigWrapper {
public final static String BLACKLISTED_SYMBOLS = "<>[]:/\\|?*\"";
public final static List<String> PRINT_CONFIG_KEYS = Arrays.asList(
"layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "slicing_mode",
"top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness",
"extra_perimeters", "extra_perimeters_on_overhangs", "avoid_crossing_curled_overhangs", "avoid_crossing_perimeters", "thin_walls", "overhangs",
"seam_position","staggered_inner_seams", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
"infill_every_layers", /*"infill_only_where_needed",*/ "solid_infill_every_layers", "fill_angle", "bridge_angle",
"solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first",
"ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing",
"max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour",
"fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_dist",
"max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
"enable_dynamic_overhang_speeds", "overhang_speed_0", "overhang_speed_1", "overhang_speed_2", "overhang_speed_3",
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
"bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "first_layer_speed_over_raft", "perimeter_acceleration", "infill_acceleration",
"external_perimeter_acceleration", "top_solid_infill_acceleration", "solid_infill_acceleration", "travel_acceleration", "wipe_tower_acceleration",
"bridge_acceleration", "first_layer_acceleration", "first_layer_acceleration_over_raft", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
"min_skirt_length", "brim_width", "brim_separation", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
"support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_closing_radius", "support_material_style",
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers",
"support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops",
"support_material_contact_distance", "support_material_bottom_contact_distance",
"support_material_buildplate_only",
"support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", "support_tree_branch_diameter_angle", "support_tree_branch_diameter_double_wall",
"support_tree_top_rate", "support_tree_branch_distance", "support_tree_tip_diameter",
"dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius",
"extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "perimeter_extruder",
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
"ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio",
"elefant_foot_compensation", "xy_size_compensation", "resolution", "gcode_resolution", "arc_fitting",
"wipe_tower", "wipe_tower_x", "wipe_tower_y",
"wipe_tower_width", "wipe_tower_cone_angle", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
"mmu_segmented_region_interlocking_depth", "wipe_tower_extruder", "wipe_tower_no_sparse_layers", "wipe_tower_extra_flow", "wipe_tower_extra_spacing", "compatible_printers", "compatible_printers_condition", "inherits",
"perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
"wall_distribution_count", "min_feature_size", "min_bead_width",
"top_one_perimeter_type", "only_one_perimeter_first_layer"
);
public final static List<String> FILAMENT_CONFIG_KEYS = Arrays.asList(
"filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed", "filament_infill_max_speed", "filament_infill_max_crossing_speed",
"extrusion_multiplier", "filament_density", "filament_cost", "filament_spool_weight", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time",
"filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves", "filament_stamping_loading_speed", "filament_stamping_distance",
"filament_cooling_initial_speed", "filament_purge_multiplier", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower",
"filament_multitool_ramming", "filament_multitool_ramming_volume", "filament_multitool_ramming_flow",
"temperature", "idle_temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed",
"max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "full_fan_speed_layer", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed",
"start_filament_gcode", "end_filament_gcode", "enable_dynamic_fan_speeds", "chamber_temperature", "chamber_minimal_temperature",
"overhang_fan_speed_0", "overhang_fan_speed_1", "overhang_fan_speed_2", "overhang_fan_speed_3",
// Retract overrides
"filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel",
"filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe", "filament_retract_length_toolchange", "filament_retract_restart_extra_toolchange", "filament_travel_ramping_lift",
"filament_travel_slope", "filament_travel_max_lift", "filament_travel_lift_before_obstacle",
// Profile compatibility
"filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits",
// Shrinkage compensation
"filament_shrinkage_compensation_xy", "filament_shrinkage_compensation_z"
);
public final static List<String> PRINTER_CONFIG_KEYS = Arrays.asList(
"printer_technology", "autoemit_temperature_commands",
"bed_shape", "bed_custom_texture", "bed_custom_model", "binary_gcode", "z_offset", "gcode_flavor", "use_relative_e_distances",
"use_firmware_retraction", "use_volumetric_e", "variable_layer_height", "prefer_clockwise_movements",
//FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset.
"host_type", "print_host", "printhost_apikey", "printhost_cafile",
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
"color_change_gcode", "pause_print_gcode", "template_custom_gcode",
"between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction",
"cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "multimaterial_purging",
"max_print_height", "default_print_profile", "inherits",
"remaining_times", "silent_mode",
"machine_limits_usage", "thumbnails", "thumbnails_format",
"machine_max_acceleration_extruding", "machine_max_acceleration_retracting", "machine_max_acceleration_travel",
"machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e",
"machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e",
"machine_min_extruding_rate", "machine_min_travel_rate",
"machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e"
);
public final static List<String> PHYSICAL_PRINTER_CONFIG_KEYS = Arrays.asList(
"preset_name", // temporary option to compatibility with older Slicer
"preset_names",
"printer_technology",
"host_type",
"print_host",
"printhost_apikey",
"printhost_cafile",
"printhost_port",
"printhost_authorization_type",
// HTTP digest authentization (RFC 2617)
"printhost_user",
"printhost_password",
"printhost_ssl_ignore_revoke"
);
private File file;
public List<ConfigObject> printConfigs = new ArrayList<>();
public List<ConfigObject> printerConfigs = new ArrayList<>();
public List<ConfigObject> filamentConfigs = new ArrayList<>();
public List<ConfigObject> physicalPrintersConfigs = new ArrayList<>();
public List<ConfigObject> printerModels = new ArrayList<>();
public ConfigObject presets;
public ConfigObject vendor;
public Slic3rConfigWrapper() {}
public Slic3rConfigWrapper(File f) throws IOException {
file = f;
readFromStream(new FileInputStream(file));
}
public Slic3rConfigWrapper(InputStream in) throws IOException {
readFromStream(in);
}
public void importPrint(ConfigObject obj) {
importInto(printConfigs, obj);
}
public void importPrinter(ConfigObject obj) {
importInto(printerConfigs, obj);
}
public void importFilament(ConfigObject obj) {
importInto(filamentConfigs, obj);
}
public void importInto(List<ConfigObject> list, ConfigObject obj) {
for (ConfigObject o : list) {
if (o.getTitle().equals(obj.getTitle())) {
o.values.clear();
o.values.putAll(obj.values);
return;
}
}
list.add(obj);
}
public ConfigObject findFilament(String key) {
for (ConfigObject obj : filamentConfigs) {
if (key.equals(obj.getTitle())) {
return obj;
}
}
return null;
}
public ConfigObject findPrinterVariant(String model, String variant) {
for (ConfigObject obj : printerConfigs) {
if (model.equals(obj.get("printer_model")) && variant.equals(obj.get("printer_variant"))) {
return obj;
}
}
return null;
}
public ConfigObject findPrint(String key) {
for (ConfigObject obj : printConfigs) {
if (key.equals(obj.getTitle())) {
return obj;
}
}
return null;
}
public ConfigObject findPrinter(String key) {
for (ConfigObject obj : printerConfigs) {
if (key.equals(obj.getTitle())) {
return obj;
}
}
return null;
}
private void serializeList(StringBuilder sb, String key, List<ConfigObject> list) {
for (ConfigObject cfg : list) {
sb.append("[").append(key).append(":").append(cfg.getTitle()).append("]\n");
for (Map.Entry<String, String> en : cfg.values.entrySet()) {
sb.append(en.getKey()).append(" = ").append(en.getValue().replace("\n", "\\n")).append("\n");
}
sb.append("\n");
}
}
public String serialize() {
StringBuilder sb = new StringBuilder();
sb.append("# generated by Slice Beam ").append(BuildConfig.VERSION_NAME).append("\n\n");
serializeList(sb, "printer", printerConfigs);
serializeList(sb, "print", printConfigs);
serializeList(sb, "filament", filamentConfigs);
if (presets != null) {
sb.append("[presets]\n");
for (Map.Entry<String, String> en : presets.values.entrySet()) {
sb.append(en.getKey()).append(" = ").append(en.getValue().replace("\n", "\\n")).append("\n");
}
}
return sb.toString();
}
public void readFromStream(InputStream in) throws IOException {
BufferedReader r = new BufferedReader(new InputStreamReader(in));
ConfigObject currentPrintConfig = null;
ConfigObject currentPrinterConfig = null;
ConfigObject currentFilamentConfig = null;
ConfigObject currentPhysicalPrinterConfig = null;
ConfigObject explicitObject = null;
Map<String, ConfigObject> parentMap = new HashMap<>();
String line;
while ((line = r.readLine()) != null) {
if (line.startsWith("#")) continue;
if (line.startsWith("[") && line.endsWith("]")) {
if (line.equals("[obsolete_presets]")) {
explicitObject = new ConfigObject();
continue;
}
if (!line.contains(":") && !line.equals("[presets]") && !line.equals("[vendor]")) {
throw new UnsupportedEncodingException(String.format("Failed to decode config category: %s", line));
}
if (currentPrintConfig != null || currentPrinterConfig != null || currentFilamentConfig != null || currentPhysicalPrinterConfig != null) {
throw new UnsupportedEncodingException("Failed to decode config: explicit category in combined profile!");
}
if (line.equals("[presets]")) {
explicitObject = presets = new ConfigObject();
continue;
}
if (line.equals("[vendor]")) {
explicitObject = vendor = new ConfigObject();
continue;
}
line = line.substring(1, line.length() - 1);
String[] spl = line.split(":");
String key = spl[0];
String name = spl[1];
switch (key) {
case "printer_model": {
printerModels.add(explicitObject = new ConfigObject(name));
break;
}
case "print": {
printConfigs.add(explicitObject = new ConfigObject(name));
explicitObject.profileListType = ConfigObject.PROFILE_LIST_PRINT;
break;
}
case "printer": {
printerConfigs.add(explicitObject = new ConfigObject(name));
explicitObject.profileListType = ConfigObject.PROFILE_LIST_PRINTER;
break;
}
case "physical_printer": {
physicalPrintersConfigs.add(explicitObject = new ConfigObject(name));
break;
}
case "filament": {
filamentConfigs.add(explicitObject = new ConfigObject(name));
explicitObject.profileListType = ConfigObject.PROFILE_LIST_FILAMENT;
break;
}
}
parentMap.put(name, explicitObject);
}
int i = line.indexOf(" = ");
if (i != -1) {
String key = line.substring(0, i);
String value = line.substring(i + 3).trim().replace("\\n", "\n");
if (key.equals("ironing_type") && value.equals("no ironing")) {
value = "top";
}
if (key.equals("thumbnails")) {
value = value.replaceAll(", \\d+x\\d+/COLPIC", "");
}
if (explicitObject != null) {
explicitObject.put(key, value);
} else {
if (key.equals("printer_settings_id")) {
if (currentPrinterConfig == null)
currentPrinterConfig = new ConfigObject();
currentPrinterConfig.setTitle(value);
}
if (key.equals("print_settings_id")) {
if (currentPrintConfig == null)
currentPrintConfig = new ConfigObject();
currentPrintConfig.setTitle(value);
}
if (key.equals("filament_settings_id")) {
if (currentFilamentConfig == null)
currentFilamentConfig = new ConfigObject();
currentFilamentConfig.setTitle(value);
}
if (PRINT_CONFIG_KEYS.contains(key)) {
if (currentPrintConfig == null)
currentPrintConfig = new ConfigObject();
currentPrintConfig.put(key, value);
}
if (FILAMENT_CONFIG_KEYS.contains(key)) {
if (currentFilamentConfig == null)
currentFilamentConfig = new ConfigObject();
currentFilamentConfig.put(key, value);
}
if (PRINTER_CONFIG_KEYS.contains(key)) {
if (currentPrinterConfig == null)
currentPrinterConfig = new ConfigObject();
currentPrinterConfig.put(key, value);
}
if (PHYSICAL_PRINTER_CONFIG_KEYS.contains(key)) {
if (currentPhysicalPrinterConfig == null)
currentPhysicalPrinterConfig = new ConfigObject();
currentPhysicalPrinterConfig.put(key, value);
}
}
}
}
for (ConfigObject obj : parentMap.values()) {
while (obj.values.containsKey("inherits")) {
String value = obj.values.remove("inherits");
if (value.isEmpty()) continue;
if (value.contains(";")) {
String[] spl = value.split(";");
for (String s : spl) {
String str = s.trim();
Map<String, String> newValues = new HashMap<>();
newValues.putAll(parentMap.get(str).values);
newValues.putAll(obj.values);
obj.values = newValues;
}
} else {
if (parentMap.containsKey(value)) {
Map<String, String> newValues = new HashMap<>();
newValues.putAll(parentMap.get(value).values);
newValues.putAll(obj.values);
obj.values = newValues;
}
}
}
}
if (currentPrintConfig != null) {
printConfigs.add(currentPrintConfig);
}
if (currentPrinterConfig != null) {
printerConfigs.add(currentPrinterConfig);
}
if (currentFilamentConfig != null) {
filamentConfigs.add(currentFilamentConfig);
}
if (currentPhysicalPrinterConfig != null) {
physicalPrintersConfigs.add(currentPhysicalPrinterConfig);
}
if (presets == null) {
presets = new ConfigObject();
if (currentPrintConfig != null) {
presets.put("print", currentPrintConfig.getTitle());
}
if (currentFilamentConfig != null) {
presets.put("filament", currentFilamentConfig.getTitle());
}
if (currentPrinterConfig != null) {
presets.put("printer", currentPrinterConfig.getTitle());
}
if (currentPhysicalPrinterConfig != null) {
presets.put("physical_printer", currentPhysicalPrinterConfig.getTitle());
}
}
r.close();
in.close();
}
}
@@ -0,0 +1,96 @@
package ru.ytkab0bp.slicebeam.slic3r;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import ru.ytkab0bp.slicebeam.SliceBeam;
public class Slic3rLocalization {
private static Map<String, Slic3rLocalization> localesMap = new HashMap<String, Slic3rLocalization>() {
@Override
public Slic3rLocalization get(@Nullable Object key) {
Slic3rLocalization locale = super.get(key);
if (locale == null) {
try {
put((String) key, locale = new Slic3rLocalization((String) key));
} catch (IOException e) {
e.printStackTrace();
try {
put((String) key, locale = new Slic3rLocalization("en"));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
return locale;
}
};
private Map<String, String> map = new HashMap<>();
public Slic3rLocalization(String key) throws IOException {
InputStream in = SliceBeam.INSTANCE.getAssets().open("localization/" + key + ".po");
BufferedReader r = new BufferedReader(new InputStreamReader(in));
String line;
StringBuilder msgId = null;
StringBuilder msgStr = null;
while ((line = r.readLine()) != null) {
if (line.startsWith("#")) continue;
if (line.startsWith("msgid")) {
msgId = new StringBuilder(line.substring(7, line.length() - 1));
} else if (line.startsWith("msgstr")) {
msgStr = new StringBuilder(line.substring(8, line.length() - 1));
} else if (line.isEmpty()) {
if (!TextUtils.isEmpty(msgId) && !TextUtils.isEmpty(msgStr)) {
// This hack allows us to maintain vanilla strings in native code while using our app name at the same time
map.put(msgId.toString(), replaceStr(msgStr.toString()));
}
msgId = null;
msgStr = null;
} else if (line.startsWith("\"") && line.endsWith("\"")) {
if (msgStr != null) {
msgStr.append(line.substring(1, line.length() - 1));
} else if (msgId != null) {
msgId.append(line.substring(1, line.length() - 1));
}
}
}
r.close();
in.close();
}
private static String replaceStr(String val) {
return val.replace("\\n", "\n").replaceAll("\\\\(.)", "$1").replace("Slic3r", "Slice Beam").replace("PrusaSlicer", "Slice Beam");
}
public static String getString(String key) {
return getInstance().get(key);
}
public String get(String key) {
String val = map.get(key);
if (TextUtils.isEmpty(val)) {
map.put(key, val = replaceStr(key));
}
return val;
}
public static Slic3rLocalization getInstance(String key) {
return localesMap.get(key);
}
public static Slic3rLocalization getInstance() {
return getInstance(Locale.getDefault().getLanguage());
}
}
@@ -0,0 +1,18 @@
package ru.ytkab0bp.slicebeam.slic3r;
public class Slic3rRuntimeError extends Exception {
public Slic3rRuntimeError() {
}
public Slic3rRuntimeError(String message) {
super(message);
}
public Slic3rRuntimeError(String message, Throwable cause) {
super(message, cause);
}
public Slic3rRuntimeError(Throwable cause) {
super(cause);
}
}
@@ -0,0 +1,46 @@
package ru.ytkab0bp.slicebeam.slic3r;
import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
import android.text.TextUtils;
import ru.ytkab0bp.slicebeam.utils.Vec3d;
public class Slic3rUtils {
public static void calcViewNormalMatrix(double[] viewMatrix, double[] worldMatrix, double[] normalMatrix) {
assertTrue(viewMatrix.length == 16);
assertTrue(worldMatrix.length == 16);
assertTrue(normalMatrix.length == 12);
Native.utils_calc_view_normal_matrix(viewMatrix, worldMatrix, normalMatrix);
}
public static Vec3d unproject(double[] viewMatrix, double[] projectionMatrix, int screenWidth, int screenHeight, double x, double y) {
assertTrue(viewMatrix.length == 16);
assertTrue(projectionMatrix.length == 16);
double[] v = Native.utils_unproject(viewMatrix, projectionMatrix, screenWidth, screenHeight, x, y);
return new Vec3d(v[0], v[1], v[2]);
}
public final static class ConfigChecker {
private final long pointer;
public ConfigChecker(String config) {
pointer = Native.utils_config_create(config);
}
public boolean checkCompatibility(String condition) {
if (TextUtils.isEmpty(condition)) return true;
return Native.utils_config_check_compatibility(pointer, condition);
}
public String eval(String condition) throws Slic3rRuntimeError {
return Native.utils_config_eval(pointer, condition);
}
public void release() {
Native.utils_config_release(pointer);
}
}
}
@@ -0,0 +1,5 @@
package ru.ytkab0bp.slicebeam.slic3r;
public interface SliceListener {
void onProgress(int progress, String text);
}