Merge branch 'master' of https://github.com/alexrj/Slic3r into release_candidate_1_3

This commit is contained in:
Vojtech Bubnik 2016-06-03 17:45:05 +02:00
commit 7649a1df41
22 changed files with 205 additions and 68 deletions

View File

@ -87,7 +87,8 @@ sub new {
$bed_sizer->Add($sizer, 1, wxEXPAND, 0); $bed_sizer->Add($sizer, 1, wxEXPAND, 0);
} }
$bed_sizer->AddSpacer(0); # XYZ home button
$move_button->($bed_sizer, 'XYZ', 'house', 1, wxTOP, sub { $self->home(undef) });
# X buttons # X buttons
{ {
@ -180,10 +181,11 @@ sub rel_move {
sub home { sub home {
my ($self, $axis) = @_; my ($self, $axis) = @_;
$axis //= '';
$self->sender->send(sprintf("G28 %s", $axis), 1); $self->sender->send(sprintf("G28 %s", $axis), 1);
$self->{canvas}->set_pos(undef); $self->{canvas}->set_pos(undef);
$self->x_homed if $axis eq 'X'; $self->x_homed(1) if $axis eq 'X';
$self->y_homed if $axis eq 'Y'; $self->y_homed(1) if $axis eq 'Y';
} }
1; 1;

View File

@ -75,7 +75,8 @@ sub BUILD {
} }
} }
} }
@mm3_per_mm = grep $_ != 0, @mm3_per_mm; # filter out 0-width segments
@mm3_per_mm = grep $_ > 0.000001, @mm3_per_mm;
if (@mm3_per_mm) { if (@mm3_per_mm) {
my $min_mm3_per_mm = min(@mm3_per_mm); my $min_mm3_per_mm = min(@mm3_per_mm);
# In order to honor max_print_speed we need to find a target volumetric # In order to honor max_print_speed we need to find a target volumetric

View File

@ -1,4 +1,4 @@
use Test::More tests => 21; use Test::More tests => 23;
use strict; use strict;
use warnings; use warnings;
@ -8,7 +8,7 @@ BEGIN {
} }
use Slic3r; use Slic3r;
use List::Util qw(first sum); use List::Util qw(first sum none);
use Slic3r::Geometry qw(epsilon scale unscale scaled_epsilon Y); use Slic3r::Geometry qw(epsilon scale unscale scaled_epsilon Y);
use Slic3r::Test; use Slic3r::Test;
@ -91,7 +91,20 @@ if (0) {
is scalar(@$res), 1, 'medial axis of a narrow rectangle with an extra vertex is still a single line'; is scalar(@$res), 1, 'medial axis of a narrow rectangle with an extra vertex is still a single line';
ok unscale($res->[0]->length) >= (200-100 - (120-100)) - epsilon, 'medial axis has still a reasonable length'; ok unscale($res->[0]->length) >= (200-100 - (120-100)) - epsilon, 'medial axis has still a reasonable length';
ok !(grep { abs($_ - scale 150) < scaled_epsilon } map $_->[Y], map @$_, @$res2), "extra vertices don't influence medial axis"; ok !(grep { abs($_ - scale 150) < scaled_epsilon } map $_->[Y], map @$_, @$res2), "extra vertices don't influence medial axis";
}
{
my $expolygon = Slic3r::ExPolygon->new(
Slic3r::Polygon->new([1185881,829367],[1421988,1578184],[1722442,2303558],[2084981,2999998],[2506843,3662186],[2984809,4285086],[3515250,4863959],[4094122,5394400],[4717018,5872368],[5379210,6294226],[6075653,6656769],[6801033,6957229],[7549842,7193328],[8316383,7363266],[9094809,7465751],[9879211,7500000],[10663611,7465750],[11442038,7363265],[12208580,7193327],[12957389,6957228],[13682769,6656768],[14379209,6294227],[15041405,5872366],[15664297,5394401],[16243171,4863960],[16758641,4301424],[17251579,3662185],[17673439,3000000],[18035980,2303556],[18336441,1578177],[18572539,829368],[18750748,0],[19758422,0],[19727293,236479],[19538467,1088188],[19276136,1920196],[18942292,2726179],[18539460,3499999],[18070731,4235755],[17539650,4927877],[16950279,5571067],[16307090,6160437],[15614974,6691519],[14879209,7160248],[14105392,7563079],[13299407,7896927],[12467399,8159255],[11615691,8348082],[10750769,8461952],[9879211,8500000],[9007652,8461952],[8142729,8348082],[7291022,8159255],[6459015,7896927],[5653029,7563079],[4879210,7160247],[4143447,6691519],[3451331,6160437],[2808141,5571066],[2218773,4927878],[1687689,4235755],[1218962,3499999],[827499,2748020],[482284,1920196],[219954,1088186],[31126,236479],[0,0],[1005754,0]),
);
my $res = $expolygon->medial_axis(scale 1.324888, scale 0.25);
is scalar(@$res), 1, 'medial axis of a semicircumference is a single line';
# check whether turns are all CCW or all CW
my @lines = @{$res->[0]->lines};
my @angles = map { $lines[$_-1]->ccw($lines[$_]->b) } 1..$#lines;
ok !!(none { $_ < 0 } @angles) || (none { $_ > 0 } @angles),
'all medial axis segments of a semicircumference have the same orientation';
} }
{ {
@ -136,7 +149,7 @@ if (0) {
{ {
my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new_scale( my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new_scale(
[50, 100], [50, 100],
[300, 102], [1000, 102],
[50, 104], [50, 104],
)); ));
my $res = $expolygon->medial_axis(scale 4, scale 0.5); my $res = $expolygon->medial_axis(scale 4, scale 0.5);

View File

@ -0,0 +1,19 @@
// title: Layer_generator
// written by: Joseph Lenox
// Used for generating cubes oriented about the center
// for making simple modifier meshes.
var width = 100;
var layer_height = 0.3;
var z = 30;
function main() {
return cube(size=[width,width,layer_height], center=true).translate([0,0,z]);
}
function getParameterDefinitions() {
return [
{ name: 'width', type: 'float', initial: 100, caption: "Width of the cube:" },
{ name: 'layer_height', type: 'float', initial: 0.3, caption: "Layer height used:" },
{ name: 'z', type: 'float', initial: 0, caption: "Z:" }
];
}

View File

@ -0,0 +1,24 @@
// Used to generate a modifier mesh to do something every few layers.
// Load into OpenSCAD, tweak the variables below, export as STL and load as
// a modifier mesh. Then change settings for the modifier mesh.
// Written by Joseph Lenox; in public domain.
layer_height = 0.3; // set to layer height in slic3r for "best" results.
number_of_solid_layers = 2;
N = 4; // N > number_of_solid_layers or else the whole thing will be solid
model_height = 300.0;
model_width = 300.0; // these two should be at least as big as the model
model_depth = 300.0; // but bigger isn't a problem
initial_offset=0; // don't generate below this
position_on_bed=[0,0,0]; // in case you need to move it around
// don't touch below unless you know what you are doing.
simple_layers = round(model_height/layer_height);
translate(position_on_bed)
for (i = [initial_offset:N:simple_layers]) {
translate([0,0,i*layer_height])
translate([0,0,(layer_height*number_of_solid_layers)/2])
cube([model_width,model_depth,layer_height*number_of_solid_layers], center=true);
}

View File

@ -0,0 +1,13 @@
#!/usr/bin/python
import sys
import re
sea = re.compile("M106 S[1-9]+[0-9]*")
rep = re.compile("M106 S255\n\g<0>")
out = open(sys.argv[1]+"_fixed", 'w')
with open(sys.argv[1]) as f:
for r in f:
if re.search(sea, r) is not None:
out.write(re.sub(sea,"M106 S255\n\g<0>",r))
else:
out.write(r)

View File

@ -174,17 +174,10 @@ void
ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polylines) const ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polylines) const
{ {
// init helper object // init helper object
Slic3r::Geometry::MedialAxis ma(max_width, min_width); Slic3r::Geometry::MedialAxis ma(max_width, min_width, this);
ma.expolygon = this; ma.lines = this->lines();
// populate list of segments for the Voronoi diagram // compute the Voronoi diagram and extract medial axis polylines
ma.lines = this->contour.lines();
for (Polygons::const_iterator hole = this->holes.begin(); hole != this->holes.end(); ++hole) {
Lines lines = hole->lines();
ma.lines.insert(ma.lines.end(), lines.begin(), lines.end());
}
// compute the Voronoi diagram
ThickPolylines pp; ThickPolylines pp;
ma.build(&pp); ma.build(&pp);
@ -195,15 +188,14 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl
svg.Close(); svg.Close();
*/ */
// find the maximum width returned /* Find the maximum width returned; we're going to use this for validating and
filtering the output segments. */
double max_w = 0; double max_w = 0;
if (!pp.empty()) { for (ThickPolylines::const_iterator it = pp.begin(); it != pp.end(); ++it)
std::vector<double> widths; max_w = fmaxf(max_w, *std::max_element(it->width.begin(), it->width.end()));
for (ThickPolylines::const_iterator it = pp.begin(); it != pp.end(); ++it)
widths.insert(widths.end(), it->width.begin(), it->width.end());
max_w = *std::max_element(widths.begin(), widths.end());
}
/* Loop through all returned polylines in order to extend their endpoints to the
expolygon boundaries */
bool removed = false; bool removed = false;
for (size_t i = 0; i < pp.size(); ++i) { for (size_t i = 0; i < pp.size(); ++i) {
ThickPolyline& polyline = pp[i]; ThickPolyline& polyline = pp[i];
@ -502,4 +494,15 @@ ExPolygon::lines() const
return lines; return lines;
} }
std::string
ExPolygon::dump_perl() const
{
std::ostringstream ret;
ret << "[" << this->contour.dump_perl();
for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h)
ret << "," << h->dump_perl();
ret << "]";
return ret.str();
}
} }

View File

@ -42,6 +42,7 @@ class ExPolygon
void triangulate_pp(Polygons* polygons) const; void triangulate_pp(Polygons* polygons) const;
void triangulate_p2t(Polygons* polygons) const; void triangulate_p2t(Polygons* polygons) const;
Lines lines() const; Lines lines() const;
std::string dump_perl() const;
}; };
} }

View File

@ -2,6 +2,7 @@
#include "ExtrusionEntity.hpp" #include "ExtrusionEntity.hpp"
#include <algorithm> #include <algorithm>
#include <cstdlib> #include <cstdlib>
#include <math.h>
namespace Slic3r { namespace Slic3r {
@ -177,7 +178,7 @@ Wipe::wipe(GCode &gcodegen, bool toolchange)
/* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one /* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one
due to rounding (TODO: test and/or better math for this) */ due to rounding (TODO: test and/or better math for this) */
double dE = length * (segment_length / wipe_dist) * 0.95; double dE = length * (segment_length / wipe_dist) * 0.95;
gcode += gcodegen.writer.set_speed(wipe_speed*60, gcodegen.enable_cooling_markers ? ";_WIPE" : ""); gcode += gcodegen.writer.set_speed(wipe_speed*60, "", gcodegen.enable_cooling_markers ? ";_WIPE" : "");
gcode += gcodegen.writer.extrude_to_xy( gcode += gcodegen.writer.extrude_to_xy(
gcodegen.point_to_gcode(line->b), gcodegen.point_to_gcode(line->b),
-dE, -dE,
@ -390,7 +391,7 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed)
Polygon polygon = loop.polygon(); Polygon polygon = loop.polygon();
Point centroid = polygon.centroid(); Point centroid = polygon.centroid();
last_pos = Point(polygon.bounding_box().max.x, centroid.y); last_pos = Point(polygon.bounding_box().max.x, centroid.y);
last_pos.rotate(rand() % 2*PI, centroid); last_pos.rotate(fmod((float)rand()/16.0, 2.0*PI), centroid);
} }
loop.split_at(last_pos); loop.split_at(last_pos);
} }
@ -562,7 +563,7 @@ GCode::_extrude(ExtrusionPath path, std::string description, double speed)
// extrude arc or line // extrude arc or line
if (path.is_bridge() && this->enable_cooling_markers) if (path.is_bridge() && this->enable_cooling_markers)
gcode += ";_BRIDGE_FAN_START\n"; gcode += ";_BRIDGE_FAN_START\n";
gcode += this->writer.set_speed(F, this->enable_cooling_markers ? ";_EXTRUDE_SET_SPEED" : ""); gcode += this->writer.set_speed(F, "", this->enable_cooling_markers ? ";_EXTRUDE_SET_SPEED" : "");
double path_length = 0; double path_length = 0;
{ {
std::string comment = this->config.gcode_comments ? description : ""; std::string comment = this->config.gcode_comments ? description : "";

View File

@ -273,11 +273,13 @@ GCodeWriter::toolchange(unsigned int extruder_id)
} }
std::string std::string
GCodeWriter::set_speed(double F, const std::string &comment) const GCodeWriter::set_speed(double F, const std::string &comment,
const std::string &cooling_marker) const
{ {
std::ostringstream gcode; std::ostringstream gcode;
gcode << "G1 F" << F; gcode << "G1 F" << F;
COMMENT(comment); COMMENT(comment);
gcode << cooling_marker;
gcode << "\n"; gcode << "\n";
return gcode.str(); return gcode.str();
} }

View File

@ -34,7 +34,7 @@ class GCodeWriter {
bool need_toolchange(unsigned int extruder_id) const; bool need_toolchange(unsigned int extruder_id) const;
std::string set_extruder(unsigned int extruder_id); std::string set_extruder(unsigned int extruder_id);
std::string toolchange(unsigned int extruder_id); std::string toolchange(unsigned int extruder_id);
std::string set_speed(double F, const std::string &comment = std::string()) const; std::string set_speed(double F, const std::string &comment = std::string(), const std::string &cooling_marker = std::string()) const;
std::string travel_to_xy(const Pointf &point, const std::string &comment = std::string()); std::string travel_to_xy(const Pointf &point, const std::string &comment = std::string());
std::string travel_to_xyz(const Pointf3 &point, const std::string &comment = std::string()); std::string travel_to_xyz(const Pointf3 &point, const std::string &comment = std::string());
std::string travel_to_z(double z, const std::string &comment = std::string()); std::string travel_to_z(double z, const std::string &comment = std::string());

View File

@ -324,7 +324,7 @@ MedialAxis::build(ThickPolylines* polylines)
if (edge->is_secondary() || edge->is_infinite()) continue; if (edge->is_secondary() || edge->is_infinite()) continue;
// don't re-validate twins // don't re-validate twins
if (seen_edges.find(&*edge) != seen_edges.end()) continue; if (seen_edges.find(&*edge) != seen_edges.end()) continue; // TODO: is this needed?
seen_edges.insert(&*edge); seen_edges.insert(&*edge);
seen_edges.insert(edge->twin()); seen_edges.insert(edge->twin());
@ -445,49 +445,66 @@ MedialAxis::validate_edge(const VD::edge_type* edge)
} }
// retrieve the original line segments which generated the edge we're checking // retrieve the original line segments which generated the edge we're checking
const VD::cell_type* cell1 = edge->cell(); const VD::cell_type* cell_l = edge->cell();
const VD::cell_type* cell2 = edge->twin()->cell(); const VD::cell_type* cell_r = edge->twin()->cell();
const Line &segment1 = this->retrieve_segment(cell1); const Line &segment_l = this->retrieve_segment(cell_l);
const Line &segment2 = this->retrieve_segment(cell2); const Line &segment_r = this->retrieve_segment(cell_r);
/* Calculate thickness of the section at both the endpoints of this edge. /*
Our Voronoi edge is part of a CCW sequence going around its Voronoi cell SVG svg("edge.svg");
(segment1). This edge's twin goes around segment2. Thus, segment2 is svg.draw(*this->expolygon);
oriented in the same direction as our main edge, and segment1 is oriented svg.draw(line);
svg.draw(segment_l, "red");
svg.draw(segment_r, "blue");
svg.Close();
*/
/* Calculate thickness of the cross-section at both the endpoints of this edge.
Our Voronoi edge is part of a CCW sequence going around its Voronoi cell
located on the left side. (segment_l).
This edge's twin goes around segment_r. Thus, segment_r is
oriented in the same direction as our main edge, and segment_l is oriented
in the same direction as our twin edge. in the same direction as our twin edge.
We used to only consider the (half-)distances to segment2, and that works We used to only consider the (half-)distances to segment_r, and that works
whenever segment1 and segment2 are almost specular and facing. However, whenever segment_l and segment_r are almost specular and facing. However,
at curves they are staggered and they only face for a very little length at curves they are staggered and they only face for a very little length
(such visibility actually coincides with our very short edge). This is why (our very short edge represents such visibility).
we calculate w0 and w1 this way. Both w0 and w1 can be calculated either towards cell_l or cell_r with equal
When cell1 or cell2 don't refer to the segment but only to an endpoint, we results by Voronoi definition.
When cell_l or cell_r don't refer to the segment but only to an endpoint, we
calculate the distance to that endpoint instead. */ calculate the distance to that endpoint instead. */
coordf_t w0 = cell2->contains_segment() coordf_t w0 = cell_r->contains_segment()
? line.a.perp_distance_to(segment2)*2 ? line.a.distance_to(segment_r)*2
: line.a.distance_to(this->retrieve_endpoint(cell2))*2; : line.a.distance_to(this->retrieve_endpoint(cell_r))*2;
coordf_t w1 = cell1->contains_segment() coordf_t w1 = cell_l->contains_segment()
? line.b.perp_distance_to(segment1)*2 ? line.b.distance_to(segment_l)*2
: line.b.distance_to(this->retrieve_endpoint(cell1))*2; : line.b.distance_to(this->retrieve_endpoint(cell_l))*2;
// if this edge is the centerline for a very thin area, we might want to skip it if (cell_l->contains_segment() && cell_r->contains_segment()) {
// in case the area is too thin // calculate the relative angle between the two boundary segments
if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON) { double angle = fabs(segment_r.orientation() - segment_l.orientation());
if (cell1->contains_segment() && cell2->contains_segment()) { if (angle > PI) angle = 2*PI - angle;
// calculate the relative angle between the two boundary segments assert(angle >= 0 && angle <= PI);
double angle = fabs(segment2.orientation() - segment1.orientation());
// fabs(angle) ranges from 0 (collinear, same direction) to PI (collinear, opposite direction)
// we're interested only in segments close to the second case (facing segments)
// so we allow some tolerance.
// this filter ensures that we're dealing with a narrow/oriented area (longer than thick)
// we don't run it on edges not generated by two segments (thus generated by one segment
// and the endpoint of another segment), since their orientation would not be meaningful
if (PI - angle > PI/8) {
// angle is not narrow enough
// fabs(angle) ranges from 0 (collinear, same direction) to PI (collinear, opposite direction) // only apply this filter to segments that are not too short otherwise their
// we're interested only in segments close to the second case (facing segments) // angle could possibly be not meaningful
// so we allow some tolerance. if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON || line.length() >= this->min_width)
// this filter ensures that we're dealing with a narrow/oriented area (longer than thick) return false;
// we don't run it on edges not generated by two segments (thus generated by one segment
// and the endpoint of another segment), since their orientation would not be meaningful
if (fabs(angle - PI) > PI/5) return false;
} else {
return false;
} }
} else {
if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON)
return false;
} }
if (w0 < this->min_width && w1 < this->min_width) if (w0 < this->min_width && w1 < this->min_width)

View File

@ -46,8 +46,8 @@ class MedialAxis {
const ExPolygon* expolygon; const ExPolygon* expolygon;
double max_width; double max_width;
double min_width; double min_width;
MedialAxis(double _max_width, double _min_width) MedialAxis(double _max_width, double _min_width, const ExPolygon* _expolygon = NULL)
: max_width(_max_width), min_width(_min_width), expolygon(NULL) {}; : max_width(_max_width), min_width(_min_width), expolygon(_expolygon) {};
void build(ThickPolylines* polylines); void build(ThickPolylines* polylines);
void build(Polylines* polylines); void build(Polylines* polylines);

View File

@ -212,6 +212,12 @@ Line::intersection(const Line& line, Point* intersection) const
return false; // not intersecting return false; // not intersecting
} }
double
Line::ccw(const Point& point) const
{
return point.ccw(*this);
}
Pointf3 Pointf3
Linef3::intersect_plane(double z) const Linef3::intersect_plane(double z) const
{ {

View File

@ -44,6 +44,7 @@ class Line
void extend_end(double distance); void extend_end(double distance);
void extend_start(double distance); void extend_start(double distance);
bool intersection(const Line& line, Point* intersection) const; bool intersection(const Line& line, Point* intersection) const;
double ccw(const Point& point) const;
}; };
class ThickLine : public Line class ThickLine : public Line

View File

@ -128,6 +128,19 @@ MultiPoint::intersection(const Line& line, Point* intersection) const
return false; return false;
} }
std::string
MultiPoint::dump_perl() const
{
std::ostringstream ret;
ret << "[";
for (Points::const_iterator p = this->points.begin(); p != this->points.end(); ++p) {
ret << p->dump_perl();
if (p != this->points.end()-1) ret << ",";
}
ret << "]";
return ret.str();
}
Points Points
MultiPoint::_douglas_peucker(const Points &points, const double tolerance) MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
{ {

View File

@ -37,6 +37,7 @@ class MultiPoint
void append(const Points &points); void append(const Points &points);
void append(const Points::const_iterator &begin, const Points::const_iterator &end); void append(const Points::const_iterator &begin, const Points::const_iterator &end);
bool intersection(const Line& line, Point* intersection) const; bool intersection(const Line& line, Point* intersection) const;
std::string dump_perl() const;
static Points _douglas_peucker(const Points &points, const double tolerance); static Points _douglas_peucker(const Points &points, const double tolerance);
}; };

View File

@ -92,7 +92,7 @@ PerimeterGenerator::process()
// the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width // the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
// (actually, something larger than that still may exist due to mitering or other causes) // (actually, something larger than that still may exist due to mitering or other causes)
coord_t min_width = scale_(this->ext_perimeter_flow.nozzle_diameter / 2); coord_t min_width = scale_(this->ext_perimeter_flow.nozzle_diameter / 3);
ExPolygons expp = offset2_ex(diffpp, -min_width/2, +min_width/2); ExPolygons expp = offset2_ex(diffpp, -min_width/2, +min_width/2);
// the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop

View File

@ -26,6 +26,14 @@ Point::wkt() const
return ss.str(); return ss.str();
} }
std::string
Point::dump_perl() const
{
std::ostringstream ss;
ss << "[" << this->x << "," << this->y << "]";
return ss.str();
}
void void
Point::scale(double factor) Point::scale(double factor)
{ {
@ -313,6 +321,14 @@ Pointf::wkt() const
return ss.str(); return ss.str();
} }
std::string
Pointf::dump_perl() const
{
std::ostringstream ss;
ss << "[" << this->x << "," << this->y << "]";
return ss.str();
}
void void
Pointf::scale(double factor) Pointf::scale(double factor)
{ {

View File

@ -38,6 +38,7 @@ class Point
}; };
bool operator==(const Point& rhs) const; bool operator==(const Point& rhs) const;
std::string wkt() const; std::string wkt() const;
std::string dump_perl() const;
void scale(double factor); void scale(double factor);
void translate(double x, double y); void translate(double x, double y);
void translate(const Vector &vector); void translate(const Vector &vector);
@ -87,6 +88,7 @@ class Pointf
return Pointf(unscale(p.x), unscale(p.y)); return Pointf(unscale(p.x), unscale(p.y));
}; };
std::string wkt() const; std::string wkt() const;
std::string dump_perl() const;
void scale(double factor); void scale(double factor);
void translate(double x, double y); void translate(double x, double y);
void translate(const Vectorf &vector); void translate(const Vectorf &vector);

View File

@ -1015,7 +1015,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("standby_temperature_delta", coInt); def = this->add("standby_temperature_delta", coInt);
def->label = "Temperature variation"; def->label = "Temperature variation";
def->tooltip = "Temperature difference to be applied when an extruder is not active."; def->tooltip = "Temperature difference to be applied when an extruder is not active. Enables a full-height \"sacrificial\" skirt on which the nozzles are periodically wiped.";
def->sidetext = "∆°C"; def->sidetext = "∆°C";
def->cli = "standby-temperature-delta=i"; def->cli = "standby-temperature-delta=i";
def->min = -500; def->min = -500;

View File

@ -41,6 +41,8 @@
%code{% RETVAL = Polyline(*THIS); %}; %code{% RETVAL = Polyline(*THIS); %};
Clone<Point> normal(); Clone<Point> normal();
Clone<Point> vector(); Clone<Point> vector();
double ccw(Point* point)
%code{% RETVAL = THIS->ccw(*point); %};
%{ %{
Line* Line*