... | ... | @@ -439,7 +439,7 @@ public: |
|
|
IMachineCodePlugin() = default;
|
|
|
~IMachineCodePlugin() override = default;
|
|
|
|
|
|
// Output file extension
|
|
|
// Output file extension or "DIR" if outputing to a folder
|
|
|
virtual std::string extension() const = 0;
|
|
|
|
|
|
// Creates and returns an Machinde Code Interface
|
... | ... | @@ -961,4 +961,232 @@ bool SamplePostProcessing::postProcessLayer(const int, const int, const bool, co |
|
|
|
|
|
The function `postProcessLayer` goes through the whole layer specifically discerning perimeters. On each perimeter, its flow is changed with the one set by the user in the printing parameter added by the plugin.
|
|
|
|
|
|
#### `SampleMachineCodePlugin` |
|
|
\ No newline at end of file |
|
|
#### `SampleMachineCodePlugin`
|
|
|
|
|
|
Finally, in the sample machine code plugin, we want to export a preview of each layer into an SVG file where each color stands for a specific type of trajectory. This is useful for debugging purposes when IceSL's path previewer isn't available or hard to discern. The sample plugin also shows these previews as UI elements, thus also exemplifying how to use the GUI functionalty of plugins.
|
|
|
|
|
|
```c++
|
|
|
// Each layer gets an SVG file. Output to a directory
|
|
|
std::string extension() const override { return "DIR"; }
|
|
|
```
|
|
|
|
|
|
We opt for a folder output, thus the `extension` function returns `"DIR"`.
|
|
|
|
|
|
```c++
|
|
|
bool SampleMachineCodePlugin::addPluginSettings(IceSLInterface::EnumerableSettingsInterface& enumerable_settings)
|
|
|
{
|
|
|
auto s = enumerable_settings.addSettingFromPlugin(
|
|
|
&m_OutputTravel, static_cast<bool*>(nullptr), static_cast<bool*>(nullptr),
|
|
|
"svg_machine_code_output_travel", "SVG show travel", "Slicing", "Draw travel lines in SVG output",
|
|
|
2,
|
|
|
[this, &enumerable_settings]() -> bool {
|
|
|
std::string machine_code;
|
|
|
enumerable_settings.getSettingByName("output_machine_code")->getValue(machine_code);
|
|
|
return machine_code == this->name();
|
|
|
}
|
|
|
);
|
|
|
return s != nullptr;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
We add a plugin parameter that allows the user to add travel information to the SVG output. Note thar his parameter is of _boolean_ type, unlike the other sample plugins.
|
|
|
|
|
|
```c++
|
|
|
bool SampleMachineCode::startWriting(const std::string& filename)
|
|
|
{
|
|
|
try {
|
|
|
|
|
|
// Get appropriate layer subset
|
|
|
bool layer_subset;
|
|
|
m_EnumerableSettings->getSettingByName("save_layer_subset")->getValue(layer_subset);
|
|
|
if (layer_subset) {
|
|
|
m_EnumerableSettings->getSettingByName("first_layer_saved")->getValue(m_FirstLayer);
|
|
|
m_EnumerableSettings->getSettingByName("number_layers_saved")->getValue(m_LastLayer);
|
|
|
m_LastLayer += m_FirstLayer;
|
|
|
}
|
|
|
// Check bounds
|
|
|
if (m_FirstLayer >= m_LayerSet->getNumLayers() || m_LastLayer > m_LayerSet->getNumLayers()) {
|
|
|
throw Fatal("[SampleMachineCode::startWriting] Out of bounds!\n1st layer: %d\nLast layer: %d\nNo. of layers: %d\n", m_FirstLayer, m_LastLayer, m_LayerSet->getNumLayers());
|
|
|
}
|
|
|
|
|
|
// Calculate tightbox
|
|
|
float nozzleDiameter;
|
|
|
for (int l = m_FirstLayer; l < m_LastLayer; l++) {
|
|
|
const IceSLInterface::ILayer& layer = m_LayerSet->getLayer(l);
|
|
|
AAB<2> bsquare;
|
|
|
for (int p = 0; p < layer.getNumPaths(); p++) {
|
|
|
const IceSLInterface::IPath& path = layer.getPath(p);
|
|
|
float nozzle;
|
|
|
m_EnumerableSettings->getSettingByName("nozzle_diameter_mm_" + std::to_string(path.getTool()))->getValue(nozzle);
|
|
|
nozzleDiameter = std::max(nozzle, nozzleDiameter);
|
|
|
for (int v = 0; v < path.getNumVertices(); v++) {
|
|
|
v2f point = m_LayerSet->xyBuildSpace(path.getVertexPos(v)); // build position
|
|
|
point = v2f(LibSL::Math::quatf(v3f(1, 0, 0), (float)M_PI).toMatrix() * v4f(point[0], point[1], 0, 1)); // Rotate 180 in X to accomodate for SVG coordinate system (Y is flipped)
|
|
|
m_TightSquare.addPoint(point);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
// Enlarge it so the perimeter trajectories are wholly visible an not only half (i.e., from the center)
|
|
|
m_TightSquare.enlarge(nozzleDiameter / 2.0f);
|
|
|
|
|
|
m_WritingLayer = m_FirstLayer;
|
|
|
m_OutputDirectory = filename;
|
|
|
m_EnumerableSettings->getSettingByName("svg_machine_code_output_travel")->getValue(m_OutputTravel);
|
|
|
|
|
|
m_EnumerableSettings->getSettingByName("bed_size_x_mm")->getValue(m_BedSize[0]);
|
|
|
m_EnumerableSettings->getSettingByName("bed_size_y_mm")->getValue(m_BedSize[1]);
|
|
|
|
|
|
} catch (Fatal& f) {
|
|
|
std::cerr << f.message() << std::endl;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Function `startWriting` does basically three things. First, it fetches all printing parameters that are needed in the plugin code. Secondly, it calculates a bounding box of the printed geometry to pass to the SVG file as appropriate viewport dimensions (SVGs require this). Lastly, it fetches the bed size to be used as viewport dimensions for the GUI elements showing the preview.
|
|
|
|
|
|
```c++
|
|
|
bool SampleMachineCode::stepWriting()
|
|
|
{
|
|
|
try {
|
|
|
if (m_WritingLayer < m_LastLayer) { // for each layer
|
|
|
// Output layer to SVG
|
|
|
char filename[256];
|
|
|
std::sprintf(filename, m_OuputFileFormat.c_str(), m_OutputDirectory.c_str(), m_WritingLayer);
|
|
|
LibSL::SvgHelpers::Svg svg(filename, m_TightSquare, LibSL::Math::m4x4f::identity(), "mm");
|
|
|
const IceSLInterface::ILayer& layer = m_LayerSet->getLayer(m_WritingLayer);
|
|
|
bool spiral = layer.isSpiral();
|
|
|
std::vector<const IceSLInterface::IPath*> travelPaths;
|
|
|
for (int p = 0; p < layer.getNumPaths(); p++) { // for each trajectory in the layer
|
|
|
const IceSLInterface::IPath& path = layer.getPath(p);
|
|
|
std::string pathType = (spiral ? "Spiral" : pathTypetoStr(path.getPathType()));
|
|
|
if (pathType == "Travel" || pathType == "Retract") {
|
|
|
if (!m_OutputTravel) continue; // No travel output
|
|
|
travelPaths.push_back(&path);
|
|
|
continue;
|
|
|
}
|
|
|
float strokeWidth = 0;
|
|
|
m_EnumerableSettings->getSettingByName("nozzle_diameter_mm_" + std::to_string(path.getTool()))->getValue(strokeWidth);
|
|
|
v3f strokeColor = m_StrokeColors.at(pathType);
|
|
|
svg.setProperties(toHexString(strokeColor), "none", strokeWidth);
|
|
|
svg.startPath();
|
|
|
for (int v = 0; v < path.getNumVertices(); v++) {
|
|
|
v2f point = m_LayerSet->xyBuildSpace(path.getVertexPos(v)); // build position
|
|
|
// Rotate 180 in X to accomodate for SVG coordinate system (Y is flipped)
|
|
|
point = v2f(LibSL::Math::quatf(v3f(1, 0, 0), (float)M_PI).toMatrix() * v4f(point[0], point[1], 0, 1));
|
|
|
svg.addPoint(point[0], point[1]);
|
|
|
}
|
|
|
svg.endPath(true);
|
|
|
}
|
|
|
// Travel paths drawn later so they are always visible
|
|
|
for (const IceSLInterface::IPath* path : travelPaths) {
|
|
|
sl_assert(path != nullptr);
|
|
|
v3f strokeColor = m_StrokeColors.at(pathTypetoStr(path->getPathType()));
|
|
|
svg.setProperties(toHexString(strokeColor), "none", 0.05f);
|
|
|
svg.startPath();
|
|
|
for (int v = 0; v < path->getNumVertices(); v++) {
|
|
|
v2f point = m_LayerSet->xyBuildSpace(path->getVertexPos(v)); // build position
|
|
|
// Rotate 180 in X to accomodate for SVG coordinate system (Y is flipped)
|
|
|
point = v2f(LibSL::Math::quatf(v3f(1, 0, 0), (float)M_PI).toMatrix() * v4f(point[0], point[1], 0, 1));
|
|
|
svg.addPoint(point[0], point[1]);
|
|
|
}
|
|
|
svg.endPath(true);
|
|
|
}
|
|
|
|
|
|
// Prepare the OpenGL render target for this layer
|
|
|
travelPaths.clear();
|
|
|
GLint currentViewport[4];
|
|
|
glGetIntegerv(GL_VIEWPORT, currentViewport); // save viewport
|
|
|
m_SlicesRT.emplace_back(new RenderTarget2DRGBA(static_cast<uint>(m_BedSize[0]), static_cast<uint>(m_BedSize[1])));
|
|
|
RenderTarget2DRGBA_Ptr RT = m_SlicesRT.back();
|
|
|
RT->bind();
|
|
|
glViewport(0, 0, RT->w(), RT->h());
|
|
|
LibSL::GPUHelpers::clearScreen(LIBSL_COLOR_BUFFER | LIBSL_DEPTH_BUFFER, 1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
m_Shader->begin();
|
|
|
m_Shader->t_matrix.set(m4x4f::identity());
|
|
|
m_Shader->p_matrix.set(orthoMatrixGL(0.0f, m_BedSize[0], -m_BedSize[1], 0.0f, -1.0f, 1.0f));
|
|
|
// Render the layer unto the render target
|
|
|
for (int p = 0; p < layer.getNumPaths(); p++) {
|
|
|
const IceSLInterface::IPath& path = layer.getPath(p);
|
|
|
std::string pathType = (spiral ? "Spiral" : pathTypetoStr(path.getPathType()));
|
|
|
if (pathType == "Travel" || pathType == "Retract") {
|
|
|
if (!m_OutputTravel) continue; // No travel output
|
|
|
travelPaths.push_back(&path);
|
|
|
continue;
|
|
|
}
|
|
|
v3f strokeColor = m_StrokeColors.at(pathType);
|
|
|
m_Shader->u_color.set(v4f(strokeColor, 1.0f));
|
|
|
m_VAO->begin(GPUMESH_LINESTRIP);
|
|
|
for (int v = 0; v < path.getNumVertices(); v++) {
|
|
|
v2f point = m_LayerSet->xyBuildSpace(path.getVertexPos(v)); // build position
|
|
|
// Rotate 180 in X to (Y is flipped)
|
|
|
point = v2f(LibSL::Math::quatf(v3f(1, 0, 0), (float)M_PI).toMatrix() * v4f(point[0], point[1], 0, 1));
|
|
|
m_VAO->vertex_3(point[0], point[1], 0.0f);
|
|
|
}
|
|
|
m_VAO->end();
|
|
|
m_VAO->render();
|
|
|
}
|
|
|
// Render the travel unto the render target
|
|
|
for (const IceSLInterface::IPath* path : travelPaths) {
|
|
|
v3f strokeColor = m_StrokeColors.at(pathTypetoStr(path->getPathType()));
|
|
|
m_Shader->u_color.set(v4f(strokeColor, 1.0f));
|
|
|
m_VAO->begin(GPUMESH_LINESTRIP);
|
|
|
for (int v = 0; v < path->getNumVertices(); v++) {
|
|
|
v2f point = m_LayerSet->xyBuildSpace(path->getVertexPos(v)); // build position
|
|
|
// Rotate 180 in X to (Y is flipped)
|
|
|
point = v2f(LibSL::Math::quatf(v3f(1, 0, 0), (float)M_PI).toMatrix() * v4f(point[0], point[1], 0, 1));
|
|
|
m_VAO->vertex_3(point[0], point[1], 0.0f);
|
|
|
}
|
|
|
m_VAO->end();
|
|
|
m_VAO->render();
|
|
|
}
|
|
|
// --
|
|
|
m_Shader->end();
|
|
|
RT->unbind();
|
|
|
// Restore viewport
|
|
|
glViewport(currentViewport[0], currentViewport[1], currentViewport[2], currentViewport[3]);
|
|
|
// Next layer
|
|
|
m_WritingLayer++;
|
|
|
} else {
|
|
|
// done
|
|
|
m_DoneSlicesRT = true;
|
|
|
return false;
|
|
|
}
|
|
|
} catch (Fatal& f) {
|
|
|
std::cerr << Console::red << f.message() << Console::gray << std::endl;
|
|
|
abortWriting();
|
|
|
return false;
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
The machine code writing function above deals with two main tasks; creating the SVG and rendering the layer onto a texture for later use by a GUI window.
|
|
|
|
|
|
Creating the SVG is composed of creating the SVG file with the appropriate parameters (e.g., viewport, units), outputting all trajectories with their appropriate colors and outputting travel paths (if enabled by the user) lastly so their always visible (superimposed on other trajectories).
|
|
|
|
|
|
Rendering the layer on a texture uses _OpenGL_ and _LibSL_ functionality that is out of the scope of this documentation. In layman's terms, what it does is creating a texture in GPU memory, setting the correct viewport based on the bed size, rendering each trajectory transformed to build space with the appropriate color and restoring the viewport (IceSL uses OpenGL too, so it needs the viewport to be correct). Just as when creating the SVG file, travel trajectories are rendered last.
|
|
|
|
|
|
```c++
|
|
|
void SampleMachineCodePlugin::gui(bool)
|
|
|
{
|
|
|
if (!m_ShowGui) { // will be true if window showing preview is closed!
|
|
|
m_SlicesRT.clear(); // clean GPU resources
|
|
|
}
|
|
|
if (!m_SlicesRT.empty()) { // show rendered layers in ImGui subwindow
|
|
|
static int shown_slice = 0;
|
|
|
ImGui::SetNextWindowSize(ImVec2(0, 0));
|
|
|
ImGui::SetNextWindowPos(ImVec2(ImGui::GetTextLineHeight(), ImGui::GetTextLineHeight() * 2), ImGuiCond_Once);
|
|
|
ImGui::Begin("SampleMachineCodePlugin", &m_ShowGui);
|
|
|
RenderTarget2DRGBA_Ptr SliceTex = m_SlicesRT.at(shown_slice);
|
|
|
ImGui::Image(reinterpret_cast<ImTextureID>(static_cast<size_t>(SliceTex->texture())), ImVec2(static_cast<float>(SliceTex->w()), static_cast<float>(SliceTex->h())));
|
|
|
ImGui::Separator();
|
|
|
ImGui::SliderInt("Layer", &shown_slice, 0, static_cast<int>(m_SlicesRT.size()) - 1);
|
|
|
ImGui::End();
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Finally, the 'gui' function shows the rendered layers in a _ImGui_ window that has a slider to navigate all layers properly. Though _ImGui_ isn't explained here, its use is straightforward. |
|
|
\ No newline at end of file |