CLIPPER_OFFSET_SCALE was made a power of two, the scaling functions
inside ClipperUtils are now using bit shifts instead of multiplication by doubles, which makes the scaling precise. Removed the scale parameter from all offset functions. Modified the safety offset to calculate offset per polygon instead of over all polygons at once. The old way was not safe and very slow, sometimes this meant a kiss of death for supports for example.
This commit is contained in:
parent
e93253e270
commit
695c92fb00
14 changed files with 214 additions and 152 deletions
|
@ -9,8 +9,51 @@
|
|||
|
||||
#include <Shiny/Shiny.h>
|
||||
|
||||
// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library
|
||||
// for general offsetting (the offset(), offset2(), offset_ex() functions) and for the safety offset,
|
||||
// which is optionally executed by other functions (union, intersection, diff).
|
||||
// By the way, is the scalling for offset needed at all?
|
||||
#define CLIPPER_OFFSET_POWER_OF_2 17
|
||||
// 2^17=131072
|
||||
#define CLIPPER_OFFSET_SCALE (1 << CLIPPER_OFFSET_POWER_OF_2)
|
||||
#define CLIPPER_OFFSET_SCALE_ROUNDING_DELTA ((1 << (CLIPPER_OFFSET_POWER_OF_2 - 1)) - 1)
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
#ifdef CLIPPER_UTILS_DEBUG
|
||||
bool clipper_export_enabled = false;
|
||||
// For debugging the Clipper library, for providing bug reports to the Clipper author.
|
||||
bool export_clipper_input_polygons_bin(const char *path, const ClipperLib::Paths &input_subject, const ClipperLib::Paths &input_clip)
|
||||
{
|
||||
FILE *pfile = fopen(path, "wb");
|
||||
if (pfile == NULL)
|
||||
return false;
|
||||
|
||||
uint32_t sz = uint32_t(input_subject.size());
|
||||
fwrite(&sz, 1, sizeof(sz), pfile);
|
||||
for (size_t i = 0; i < input_subject.size(); ++i) {
|
||||
const ClipperLib::Path &path = input_subject[i];
|
||||
sz = uint32_t(path.size());
|
||||
::fwrite(&sz, 1, sizeof(sz), pfile);
|
||||
::fwrite(path.data(), sizeof(ClipperLib::IntPoint), sz, pfile);
|
||||
}
|
||||
sz = uint32_t(input_clip.size());
|
||||
::fwrite(&sz, 1, sizeof(sz), pfile);
|
||||
for (size_t i = 0; i < input_clip.size(); ++i) {
|
||||
const ClipperLib::Path &path = input_clip[i];
|
||||
sz = uint32_t(path.size());
|
||||
::fwrite(&sz, 1, sizeof(sz), pfile);
|
||||
::fwrite(path.data(), sizeof(ClipperLib::IntPoint), sz, pfile);
|
||||
}
|
||||
::fclose(pfile);
|
||||
return true;
|
||||
|
||||
err:
|
||||
::fclose(pfile);
|
||||
return false;
|
||||
}
|
||||
#endif /* CLIPPER_UTILS_DEBUG */
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// legacy code from Clipper documentation
|
||||
void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons* expolygons)
|
||||
|
@ -120,37 +163,51 @@ Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths* output)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
scaleClipperPolygon(ClipperLib::Path &polygon, const double scale)
|
||||
void scaleClipperPolygon(ClipperLib::Path &polygon)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
|
||||
for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) {
|
||||
//FIXME multiplication of int64_t by double!
|
||||
// Replace by bit shifts?
|
||||
(*pit).X *= scale;
|
||||
(*pit).Y *= scale;
|
||||
pit->X <<= CLIPPER_OFFSET_POWER_OF_2;
|
||||
pit->Y <<= CLIPPER_OFFSET_POWER_OF_2;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale)
|
||||
void scaleClipperPolygons(ClipperLib::Paths &polygons)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
|
||||
for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) {
|
||||
for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it)
|
||||
for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) {
|
||||
//FIXME multiplication of int64_t by double!
|
||||
// Replace by bit shifts?
|
||||
(*pit).X *= scale;
|
||||
(*pit).Y *= scale;
|
||||
pit->X <<= CLIPPER_OFFSET_POWER_OF_2;
|
||||
pit->Y <<= CLIPPER_OFFSET_POWER_OF_2;
|
||||
}
|
||||
}
|
||||
|
||||
void unscaleClipperPolygon(ClipperLib::Path &polygon)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) {
|
||||
pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
|
||||
pit->Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
|
||||
pit->X >>= CLIPPER_OFFSET_POWER_OF_2;
|
||||
pit->Y >>= CLIPPER_OFFSET_POWER_OF_2;
|
||||
}
|
||||
}
|
||||
|
||||
void unscaleClipperPolygons(ClipperLib::Paths &polygons)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it)
|
||||
for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) {
|
||||
pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
|
||||
pit->Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
|
||||
pit->X >>= CLIPPER_OFFSET_POWER_OF_2;
|
||||
pit->Y >>= CLIPPER_OFFSET_POWER_OF_2;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
// read input
|
||||
|
@ -158,12 +215,12 @@ offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float
|
|||
Slic3rMultiPoints_to_ClipperPaths(polygons, &input);
|
||||
|
||||
// scale input
|
||||
scaleClipperPolygons(input, scale);
|
||||
scaleClipperPolygons(input);
|
||||
|
||||
// perform offset
|
||||
ClipperLib::ClipperOffset co;
|
||||
if (joinType == jtRound) {
|
||||
co.ArcTolerance = miterLimit;
|
||||
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
|
||||
} else {
|
||||
co.MiterLimit = miterLimit;
|
||||
}
|
||||
|
@ -173,20 +230,20 @@ offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float
|
|||
}
|
||||
{
|
||||
PROFILE_BLOCK(offset_Execute);
|
||||
co.Execute(*retval, (delta*scale));
|
||||
co.Execute(*retval, delta * float(CLIPPER_OFFSET_SCALE));
|
||||
}
|
||||
|
||||
// unscale output
|
||||
scaleClipperPolygons(*retval, 1/scale);
|
||||
unscaleClipperPolygons(*retval);
|
||||
}
|
||||
|
||||
void
|
||||
offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
// perform offset
|
||||
ClipperLib::Paths output;
|
||||
offset(polygons, &output, delta, scale, joinType, miterLimit);
|
||||
offset(polygons, &output, delta, joinType, miterLimit);
|
||||
|
||||
// convert into ExPolygons
|
||||
ClipperPaths_to_Slic3rMultiPoints(output, retval);
|
||||
|
@ -194,45 +251,45 @@ offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float d
|
|||
|
||||
Slic3r::Polygons
|
||||
offset(const Slic3r::Polygons &polygons, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
Slic3r::Polygons pp;
|
||||
offset(polygons, &pp, delta, scale, joinType, miterLimit);
|
||||
offset(polygons, &pp, delta, joinType, miterLimit);
|
||||
return pp;
|
||||
}
|
||||
|
||||
void
|
||||
offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
// read input
|
||||
ClipperLib::Paths input;
|
||||
Slic3rMultiPoints_to_ClipperPaths(polylines, &input);
|
||||
|
||||
// scale input
|
||||
scaleClipperPolygons(input, scale);
|
||||
scaleClipperPolygons(input);
|
||||
|
||||
// perform offset
|
||||
ClipperLib::ClipperOffset co;
|
||||
if (joinType == jtRound) {
|
||||
co.ArcTolerance = miterLimit;
|
||||
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
|
||||
} else {
|
||||
co.MiterLimit = miterLimit;
|
||||
}
|
||||
co.AddPaths(input, joinType, ClipperLib::etOpenButt);
|
||||
co.Execute(*retval, (delta*scale));
|
||||
co.Execute(*retval, delta * float(CLIPPER_OFFSET_SCALE));
|
||||
|
||||
// unscale output
|
||||
scaleClipperPolygons(*retval, 1/scale);
|
||||
unscaleClipperPolygons(*retval);
|
||||
}
|
||||
|
||||
void
|
||||
offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
// perform offset
|
||||
ClipperLib::Paths output;
|
||||
offset(polylines, &output, delta, scale, joinType, miterLimit);
|
||||
offset(polylines, &output, delta, joinType, miterLimit);
|
||||
|
||||
// convert into ExPolygons
|
||||
ClipperPaths_to_Slic3rMultiPoints(output, retval);
|
||||
|
@ -240,11 +297,11 @@ offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float
|
|||
|
||||
void
|
||||
offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
// perform offset
|
||||
Slic3r::ExPolygons expp;
|
||||
offset(surface.expolygon, &expp, delta, scale, joinType, miterLimit);
|
||||
offset(surface.expolygon, &expp, delta, joinType, miterLimit);
|
||||
|
||||
// clone the input surface for each expolygon we got
|
||||
retval->clear();
|
||||
|
@ -258,11 +315,11 @@ offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float del
|
|||
|
||||
void
|
||||
offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
// perform offset
|
||||
ClipperLib::Paths output;
|
||||
offset(polygons, &output, delta, scale, joinType, miterLimit);
|
||||
offset(polygons, &output, delta, joinType, miterLimit);
|
||||
|
||||
// convert into ExPolygons
|
||||
ClipperPaths_to_Slic3rExPolygons(output, retval);
|
||||
|
@ -272,19 +329,19 @@ offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float
|
|||
// a single polygon with multiple non-overlapping holes.
|
||||
// Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours.
|
||||
void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
// printf("new ExPolygon offset\n");
|
||||
// 1) Offset the outer contour.
|
||||
const float delta_scaled = float(delta * scale);
|
||||
const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE);
|
||||
ClipperLib::Paths contours;
|
||||
{
|
||||
ClipperLib::Path input;
|
||||
Slic3rMultiPoint_to_ClipperPath(expolygon.contour, &input);
|
||||
scaleClipperPolygon(input, scale);
|
||||
scaleClipperPolygon(input);
|
||||
ClipperLib::ClipperOffset co;
|
||||
if (joinType == jtRound)
|
||||
co.ArcTolerance = miterLimit;
|
||||
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
|
||||
else
|
||||
co.MiterLimit = miterLimit;
|
||||
co.AddPath(input, joinType, ClipperLib::etClosedPolygon);
|
||||
|
@ -298,10 +355,10 @@ void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const
|
|||
for (Polygons::const_iterator it_hole = expolygon.holes.begin(); it_hole != expolygon.holes.end(); ++ it_hole) {
|
||||
ClipperLib::Path input;
|
||||
Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole, &input);
|
||||
scaleClipperPolygon(input, scale);
|
||||
scaleClipperPolygon(input);
|
||||
ClipperLib::ClipperOffset co;
|
||||
if (joinType == jtRound)
|
||||
co.ArcTolerance = miterLimit;
|
||||
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
|
||||
else
|
||||
co.MiterLimit = miterLimit;
|
||||
co.AddPath(input, joinType, ClipperLib::etClosedPolygon);
|
||||
|
@ -322,15 +379,15 @@ void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const
|
|||
}
|
||||
|
||||
// 4) Unscale the output.
|
||||
scaleClipperPolygons(*retval, 1/scale);
|
||||
unscaleClipperPolygons(*retval);
|
||||
}
|
||||
|
||||
Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
// perform offset
|
||||
ClipperLib::Paths output;
|
||||
offset(expolygon, &output, delta, scale, joinType, miterLimit);
|
||||
offset(expolygon, &output, delta, joinType, miterLimit);
|
||||
|
||||
// convert into ExPolygons
|
||||
Slic3r::Polygons retval;
|
||||
|
@ -339,11 +396,11 @@ Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta,
|
|||
}
|
||||
|
||||
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
// perform offset
|
||||
ClipperLib::Paths output;
|
||||
offset(expolygon, &output, delta, scale, joinType, miterLimit);
|
||||
offset(expolygon, &output, delta, joinType, miterLimit);
|
||||
|
||||
// convert into ExPolygons
|
||||
Slic3r::ExPolygons retval;
|
||||
|
@ -355,10 +412,10 @@ Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float del
|
|||
// a single polygon with multiple non-overlapping holes.
|
||||
// Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours.
|
||||
void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
// printf("new ExPolygon offset\n");
|
||||
const float delta_scaled = float(delta * scale);
|
||||
const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE);
|
||||
ClipperLib::Paths contours;
|
||||
ClipperLib::Paths holes;
|
||||
contours.reserve(expolygons.size());
|
||||
|
@ -374,10 +431,10 @@ void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, con
|
|||
{
|
||||
ClipperLib::Path input;
|
||||
Slic3rMultiPoint_to_ClipperPath(it_expoly->contour, &input);
|
||||
scaleClipperPolygon(input, scale);
|
||||
scaleClipperPolygon(input);
|
||||
ClipperLib::ClipperOffset co;
|
||||
if (joinType == jtRound)
|
||||
co.ArcTolerance = miterLimit;
|
||||
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
|
||||
else
|
||||
co.MiterLimit = miterLimit;
|
||||
co.AddPath(input, joinType, ClipperLib::etClosedPolygon);
|
||||
|
@ -391,10 +448,10 @@ void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, con
|
|||
for (Polygons::const_iterator it_hole = it_expoly->holes.begin(); it_hole != it_expoly->holes.end(); ++ it_hole) {
|
||||
ClipperLib::Path input;
|
||||
Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole, &input);
|
||||
scaleClipperPolygon(input, scale);
|
||||
scaleClipperPolygon(input);
|
||||
ClipperLib::ClipperOffset co;
|
||||
if (joinType == jtRound)
|
||||
co.ArcTolerance = miterLimit;
|
||||
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
|
||||
else
|
||||
co.MiterLimit = miterLimit;
|
||||
co.AddPath(input, joinType, ClipperLib::etClosedPolygon);
|
||||
|
@ -416,15 +473,15 @@ void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, con
|
|||
}
|
||||
|
||||
// 4) Unscale the output.
|
||||
scaleClipperPolygons(*retval, 1/scale);
|
||||
unscaleClipperPolygons(*retval);
|
||||
}
|
||||
|
||||
Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
// perform offset
|
||||
ClipperLib::Paths output;
|
||||
offset(expolygons, &output, delta, scale, joinType, miterLimit);
|
||||
offset(expolygons, &output, delta, joinType, miterLimit);
|
||||
|
||||
// convert into ExPolygons
|
||||
Slic3r::Polygons retval;
|
||||
|
@ -433,11 +490,11 @@ Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta,
|
|||
}
|
||||
|
||||
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
// perform offset
|
||||
ClipperLib::Paths output;
|
||||
offset(expolygons, &output, delta, scale, joinType, miterLimit);
|
||||
offset(expolygons, &output, delta, joinType, miterLimit);
|
||||
|
||||
// convert into ExPolygons
|
||||
Slic3r::ExPolygons retval;
|
||||
|
@ -447,20 +504,20 @@ Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float d
|
|||
|
||||
Slic3r::ExPolygons
|
||||
offset_ex(const Slic3r::Polygons &polygons, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
Slic3r::ExPolygons expp;
|
||||
offset(polygons, &expp, delta, scale, joinType, miterLimit);
|
||||
offset(polygons, &expp, delta, joinType, miterLimit);
|
||||
return expp;
|
||||
}
|
||||
|
||||
void
|
||||
offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1,
|
||||
const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
|
||||
const float delta2, const ClipperLib::JoinType joinType, const double miterLimit)
|
||||
{
|
||||
if (delta1 * delta2 >= 0) {
|
||||
// Both deltas are the same signum
|
||||
offset(polygons, retval, delta1 + delta2, scale, joinType, miterLimit);
|
||||
offset(polygons, retval, delta1 + delta2, joinType, miterLimit);
|
||||
return;
|
||||
}
|
||||
#ifdef CLIPPER_UTILS_DEBUG
|
||||
|
@ -479,12 +536,12 @@ offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float
|
|||
Slic3rMultiPoints_to_ClipperPaths(polygons, &input);
|
||||
|
||||
// scale input
|
||||
scaleClipperPolygons(input, scale);
|
||||
scaleClipperPolygons(input);
|
||||
|
||||
// prepare ClipperOffset object
|
||||
ClipperLib::ClipperOffset co;
|
||||
if (joinType == jtRound) {
|
||||
co.ArcTolerance = miterLimit;
|
||||
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
|
||||
} else {
|
||||
co.MiterLimit = miterLimit;
|
||||
}
|
||||
|
@ -492,30 +549,30 @@ offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float
|
|||
// perform first offset
|
||||
ClipperLib::Paths output1;
|
||||
co.AddPaths(input, joinType, ClipperLib::etClosedPolygon);
|
||||
co.Execute(output1, (delta1*scale));
|
||||
co.Execute(output1, delta1 * float(CLIPPER_OFFSET_SCALE));
|
||||
#ifdef CLIPPER_UTILS_DEBUG
|
||||
svg.draw(output1, 1./CLIPPER_OFFSET_SCALE, "red", stroke_width);
|
||||
svg.draw(output1, 1. / double(CLIPPER_OFFSET_SCALE), "red", stroke_width);
|
||||
#endif /* CLIPPER_UTILS_DEBUG */
|
||||
|
||||
// perform second offset
|
||||
co.Clear();
|
||||
co.AddPaths(output1, joinType, ClipperLib::etClosedPolygon);
|
||||
co.Execute(*retval, (delta2*scale));
|
||||
co.Execute(*retval, delta2 * float(CLIPPER_OFFSET_SCALE));
|
||||
#ifdef CLIPPER_UTILS_DEBUG
|
||||
svg.draw(*retval, 1./CLIPPER_OFFSET_SCALE, "green", stroke_width);
|
||||
svg.draw(*retval, 1. / double(CLIPPER_OFFSET_SCALE), "green", stroke_width);
|
||||
#endif /* CLIPPER_UTILS_DEBUG */
|
||||
|
||||
// unscale output
|
||||
scaleClipperPolygons(*retval, 1/scale);
|
||||
unscaleClipperPolygons(*retval);
|
||||
}
|
||||
|
||||
void
|
||||
offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1,
|
||||
const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
|
||||
const float delta2, const ClipperLib::JoinType joinType, const double miterLimit)
|
||||
{
|
||||
// perform offset
|
||||
ClipperLib::Paths output;
|
||||
offset2(polygons, &output, delta1, delta2, scale, joinType, miterLimit);
|
||||
offset2(polygons, &output, delta1, delta2, joinType, miterLimit);
|
||||
|
||||
// convert into ExPolygons
|
||||
ClipperPaths_to_Slic3rMultiPoints(output, retval);
|
||||
|
@ -523,20 +580,20 @@ offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float
|
|||
|
||||
Slic3r::Polygons
|
||||
offset2(const Slic3r::Polygons &polygons, const float delta1,
|
||||
const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
|
||||
const float delta2, const ClipperLib::JoinType joinType, const double miterLimit)
|
||||
{
|
||||
Slic3r::Polygons pp;
|
||||
offset2(polygons, &pp, delta1, delta2, scale, joinType, miterLimit);
|
||||
offset2(polygons, &pp, delta1, delta2, joinType, miterLimit);
|
||||
return pp;
|
||||
}
|
||||
|
||||
void
|
||||
offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1,
|
||||
const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
|
||||
const float delta2, const ClipperLib::JoinType joinType, const double miterLimit)
|
||||
{
|
||||
// perform offset
|
||||
ClipperLib::Paths output;
|
||||
offset2(polygons, &output, delta1, delta2, scale, joinType, miterLimit);
|
||||
offset2(polygons, &output, delta1, delta2, joinType, miterLimit);
|
||||
|
||||
// convert into ExPolygons
|
||||
ClipperPaths_to_Slic3rExPolygons(output, retval);
|
||||
|
@ -544,10 +601,10 @@ offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const floa
|
|||
|
||||
Slic3r::ExPolygons
|
||||
offset2_ex(const Slic3r::Polygons &polygons, const float delta1,
|
||||
const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
|
||||
const float delta2, const ClipperLib::JoinType joinType, const double miterLimit)
|
||||
{
|
||||
Slic3r::ExPolygons expp;
|
||||
offset2(polygons, &expp, delta1, delta2, scale, joinType, miterLimit);
|
||||
offset2(polygons, &expp, delta1, delta2, joinType, miterLimit);
|
||||
return expp;
|
||||
}
|
||||
|
||||
|
@ -580,6 +637,13 @@ void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &su
|
|||
PROFILE_BLOCK(_clipper_do_polygons_AddPaths);
|
||||
clipper.AddPaths(input_subject, ClipperLib::ptSubject, true);
|
||||
clipper.AddPaths(input_clip, ClipperLib::ptClip, true);
|
||||
|
||||
#ifdef CLIPPER_UTILS_DEBUG
|
||||
if (clipper_export_enabled) {
|
||||
static int iRun = 0;
|
||||
export_clipper_input_polygons_bin(debug_out_path("_clipper_do_polygons_AddPaths-polygons-%d", ++iRun).c_str(), input_subject, input_clip);
|
||||
}
|
||||
#endif /* CLIPPER_UTILS_DEBUG */
|
||||
}
|
||||
|
||||
// perform operation
|
||||
|
@ -986,22 +1050,39 @@ void safety_offset(ClipperLib::Paths* paths)
|
|||
PROFILE_FUNC();
|
||||
|
||||
// scale input
|
||||
scaleClipperPolygons(*paths, CLIPPER_OFFSET_SCALE);
|
||||
scaleClipperPolygons(*paths);
|
||||
|
||||
// perform offset (delta = scale 1e-05)
|
||||
ClipperLib::ClipperOffset co;
|
||||
co.MiterLimit = 2;
|
||||
{
|
||||
PROFILE_BLOCK(safety_offset_AddPaths);
|
||||
co.AddPaths(*paths, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
|
||||
}
|
||||
{
|
||||
PROFILE_BLOCK(safety_offset_Execute);
|
||||
co.Execute(*paths, 10.0 * CLIPPER_OFFSET_SCALE);
|
||||
#ifdef CLIPPER_UTILS_DEBUG
|
||||
if (clipper_export_enabled) {
|
||||
static int iRun = 0;
|
||||
export_clipper_input_polygons_bin(debug_out_path("safety_offset-polygons-%d", ++iRun).c_str(), *paths, ClipperLib::Paths());
|
||||
}
|
||||
#endif /* CLIPPER_UTILS_DEBUG */
|
||||
ClipperLib::Paths out;
|
||||
for (size_t i = 0; i < paths->size(); ++ i) {
|
||||
co.Clear();
|
||||
co.MiterLimit = 2;
|
||||
{
|
||||
PROFILE_BLOCK(safety_offset_AddPaths);
|
||||
co.AddPath((*paths)[i], ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
|
||||
}
|
||||
{
|
||||
PROFILE_BLOCK(safety_offset_Execute);
|
||||
// offset outside by 10um
|
||||
ClipperLib::Paths out_this;
|
||||
co.Execute(out_this, 10.f * float(CLIPPER_OFFSET_SCALE));
|
||||
if (out.empty())
|
||||
out = std::move(out_this);
|
||||
else
|
||||
std::move(std::begin(out_this), std::end(out_this), std::back_inserter(out));
|
||||
}
|
||||
}
|
||||
*paths = std::move(out);
|
||||
|
||||
// unscale output
|
||||
scaleClipperPolygons(*paths, 1.0/CLIPPER_OFFSET_SCALE);
|
||||
unscaleClipperPolygons(*paths);
|
||||
}
|
||||
|
||||
Polygons top_level_islands(const Slic3r::Polygons &polygons)
|
||||
|
|
|
@ -14,13 +14,6 @@ using ClipperLib::jtSquare;
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library.
|
||||
//FIXME Vojtech: Better to use a power of 2 coefficient and to use bit shifts for scaling.
|
||||
// How about 2^17=131072?
|
||||
// By the way, is the scalling needed at all? Cura runs all the computation with a fixed point precision of 1um, while Slic3r scales to 1nm,
|
||||
// further scaling by 10e5 brings us to
|
||||
#define CLIPPER_OFFSET_SCALE 100000.0
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// legacy code from Clipper documentation
|
||||
void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons);
|
||||
|
@ -40,29 +33,29 @@ void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale);
|
|||
|
||||
// offset Polygons
|
||||
void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
|
||||
// This is a safe variant of the polygon offset, tailored for a single ExPolygon:
|
||||
// a single polygon with multiple non-overlapping holes.
|
||||
// Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours.
|
||||
void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit);
|
||||
|
@ -71,36 +64,36 @@ Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float d
|
|||
|
||||
// offset Polylines
|
||||
void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
|
||||
ClipperLib::JoinType joinType = ClipperLib::jtSquare,
|
||||
double miterLimit = 3);
|
||||
void offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
|
||||
ClipperLib::JoinType joinType = ClipperLib::jtSquare,
|
||||
double miterLimit = 3);
|
||||
void offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
|
||||
ClipperLib::JoinType joinType = ClipperLib::jtSquare,
|
||||
double miterLimit = 3);
|
||||
|
||||
void offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
|
||||
void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1,
|
||||
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
void offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1,
|
||||
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1,
|
||||
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
void offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1,
|
||||
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1,
|
||||
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
|
||||
template <class T>
|
||||
|
|
|
@ -372,11 +372,9 @@ public:
|
|||
bool sticks_removed = remove_sticks(polygons_src);
|
||||
// if (sticks_removed) printf("Sticks removed!\n");
|
||||
polygons_outer = offset(polygons_src, aoffset1,
|
||||
CLIPPER_OFFSET_SCALE,
|
||||
ClipperLib::jtMiter,
|
||||
mitterLimit);
|
||||
polygons_inner = offset(polygons_outer, aoffset2 - aoffset1,
|
||||
CLIPPER_OFFSET_SCALE,
|
||||
ClipperLib::jtMiter,
|
||||
mitterLimit);
|
||||
// Filter out contours with zero area or small area, contours with 2 points only.
|
||||
|
|
|
@ -91,9 +91,9 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection*
|
|||
g.process();
|
||||
}
|
||||
|
||||
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 3.
|
||||
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 1.5
|
||||
#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare, 0.
|
||||
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
|
||||
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
|
||||
#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
|
||||
|
||||
void
|
||||
LayerRegion::process_external_surfaces(const Layer* lower_layer)
|
||||
|
@ -194,7 +194,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
|
|||
break;
|
||||
}
|
||||
// Grown by 3mm.
|
||||
Polygons polys = offset(bridges[i].expolygon, float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS);
|
||||
Polygons polys = offset(to_polygons(bridges[i].expolygon), float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS);
|
||||
if (idx_island == -1) {
|
||||
printf("Bridge did not fall into the source region!\r\n");
|
||||
} else {
|
||||
|
|
|
@ -610,7 +610,7 @@ Print::validate() const
|
|||
// grow convex hull with the clearance margin
|
||||
{
|
||||
Polygons grown_hull;
|
||||
offset(convex_hull, &grown_hull, scale_(this->config.extruder_clearance_radius.value)/2, 1, jtRound, scale_(0.1));
|
||||
offset(convex_hull, &grown_hull, scale_(this->config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1));
|
||||
convex_hull = grown_hull.front();
|
||||
}
|
||||
|
||||
|
|
|
@ -692,8 +692,7 @@ PrintObject::discover_vertical_shells()
|
|||
#if 1
|
||||
// Intentionally inflate a bit more than how much the region has been shrunk,
|
||||
// so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill).
|
||||
shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing,
|
||||
CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare);
|
||||
shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare);
|
||||
if (shell.empty())
|
||||
continue;
|
||||
#else
|
||||
|
@ -705,7 +704,7 @@ PrintObject::discover_vertical_shells()
|
|||
// get a triangle in $too_narrow; if we grow it below then the shell
|
||||
// would have a different shape from the external surface and we'd still
|
||||
// have the same angle, so the next shell would be grown even more and so on.
|
||||
Polygons too_narrow = diff(shell, offset2(shell, -margin, margin, CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 5.), true);
|
||||
Polygons too_narrow = diff(shell, offset2(shell, -margin, margin, ClipperLib::jtMiter, 5.), true);
|
||||
if (! too_narrow.empty()) {
|
||||
// grow the collapsing parts and add the extra area to the neighbor layer
|
||||
// as well as to our original surfaces so that we support this
|
||||
|
|
|
@ -16,58 +16,53 @@ _constant()
|
|||
JT_MITER = jtMiter
|
||||
JT_ROUND = jtRound
|
||||
JT_SQUARE = jtSquare
|
||||
CLIPPER_OFFSET_SCALE = CLIPPER_OFFSET_SCALE
|
||||
CODE:
|
||||
RETVAL = ix;
|
||||
OUTPUT: RETVAL
|
||||
|
||||
Polygons
|
||||
offset(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
offset(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
Polygons polygons
|
||||
const float delta
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset(polygons, &RETVAL, delta, scale, joinType, miterLimit);
|
||||
offset(polygons, &RETVAL, delta, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
ExPolygons
|
||||
offset_ex(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
offset_ex(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
Polygons polygons
|
||||
const float delta
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset(polygons, &RETVAL, delta, scale, joinType, miterLimit);
|
||||
offset(polygons, &RETVAL, delta, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
Polygons
|
||||
offset2(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
offset2(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
Polygons polygons
|
||||
const float delta1
|
||||
const float delta2
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit);
|
||||
offset2(polygons, &RETVAL, delta1, delta2, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
ExPolygons
|
||||
offset2_ex(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
offset2_ex(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
Polygons polygons
|
||||
const float delta1
|
||||
const float delta2
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit);
|
||||
offset2(polygons, &RETVAL, delta1, delta2, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
|
|
@ -80,13 +80,12 @@ Polyline::rotate(angle, center_sv)
|
|||
THIS->rotate(angle, center);
|
||||
|
||||
Polygons
|
||||
Polyline::grow(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtSquare, miterLimit = 3)
|
||||
Polyline::grow(delta, joinType = ClipperLib::jtSquare, miterLimit = 3)
|
||||
const float delta
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit);
|
||||
offset(*THIS, &RETVAL, delta, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
|
|
@ -83,13 +83,12 @@ Surface::polygons()
|
|||
RETVAL
|
||||
|
||||
Surfaces
|
||||
Surface::offset(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
Surface::offset(delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
const float delta
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit);
|
||||
offset(*THIS, &RETVAL, delta, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue