mirror of https://github.com/vitalif/openscad
Experimental circular/spherical/cylindric bend modifier (issue openscad/openscad#815)
parent
045a3b93b0
commit
80abdc1da0
|
@ -254,6 +254,7 @@ HEADERS += src/typedefs.h \
|
|||
src/context.h \
|
||||
src/modcontext.h \
|
||||
src/evalcontext.h \
|
||||
src/bendnode.h \
|
||||
src/csgterm.h \
|
||||
src/csgtermnormalizer.h \
|
||||
src/dxfdata.h \
|
||||
|
@ -359,6 +360,7 @@ SOURCES += src/version_check.cc \
|
|||
src/polyset-gl.cc \
|
||||
src/csgops.cc \
|
||||
src/transform.cc \
|
||||
src/bend.cc \
|
||||
src/color.cc \
|
||||
src/primitives.cc \
|
||||
src/projection.cc \
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "state.h"
|
||||
#include "offsetnode.h"
|
||||
#include "transformnode.h"
|
||||
#include "bendnode.h"
|
||||
#include "linearextrudenode.h"
|
||||
#include "rotateextrudenode.h"
|
||||
#include "csgnode.h"
|
||||
|
@ -532,6 +533,164 @@ Response GeometryEvaluator::visit(State &state, const CsgNode &node)
|
|||
return ContinueTraversal;
|
||||
}
|
||||
|
||||
/*!
|
||||
input: List of 2D or 3D objects (not mixed)
|
||||
output: Polygon2d or 3D PolySet
|
||||
operation:
|
||||
o Union all children
|
||||
o Perform bend
|
||||
*/
|
||||
Response GeometryEvaluator::visit(State &state, const BendNode &node)
|
||||
{
|
||||
#define EPS 1e-3
|
||||
if (state.isPrefix() && isSmartCached(node)) return PruneTraversal;
|
||||
if (state.isPostfix()) {
|
||||
shared_ptr<const class Geometry> geom;
|
||||
if (!isSmartCached(node)) {
|
||||
double b1[3], m, b2[3], b3[3], ov[3], R0, R, Y, L, a;
|
||||
// First union all children
|
||||
ResultObject res = applyToChildren(node, OPENSCAD_UNION);
|
||||
if ((geom = res.constptr())) {
|
||||
if (geom->getDimension() == 2) {
|
||||
// 2D circular bend, ignoring Z coord
|
||||
|
||||
// b1: radius
|
||||
b1[0] = node.fixed_x-node.center_x;
|
||||
b1[1] = node.fixed_y-node.center_y;
|
||||
R0 = sqrt(b1[0]*b1[0] + b1[1]*b1[1]);
|
||||
if (R0 < EPS) {
|
||||
PRINT("Error: 2D bend center is equal to fixed point, skipping object.");
|
||||
return ContinueTraversal;
|
||||
}
|
||||
b1[0] = b1[0]/R0;
|
||||
b1[1] = b1[1]/R0;
|
||||
|
||||
shared_ptr<const Polygon2d> polygons = dynamic_pointer_cast<const Polygon2d>(geom);
|
||||
assert(polygons);
|
||||
|
||||
// If we got a const object, make a copy
|
||||
shared_ptr<Polygon2d> newpoly;
|
||||
if (res.isConst()) newpoly.reset(new Polygon2d(*polygons));
|
||||
else newpoly = dynamic_pointer_cast<Polygon2d>(res.ptr());
|
||||
|
||||
BOOST_FOREACH(Outline2d &p, (Polygon2d::Outlines2d&)newpoly->outlines()) {
|
||||
BOOST_FOREACH(Vector2d &v, p.vertices) {
|
||||
ov[0] = v[0]-node.center_x;
|
||||
ov[1] = v[1]-node.center_y;
|
||||
R = ov[0]*b1[0] + ov[1]*b1[1];
|
||||
L = -ov[0]*b1[1] + ov[1]*b1[0];
|
||||
a = L/R0;
|
||||
v[0] = node.center_x + R*b1[0]*cos(a) - R*b1[1]*sin(a);
|
||||
v[1] = node.center_y + R*b1[1]*cos(a) + R*b1[0]*sin(a);
|
||||
}
|
||||
}
|
||||
|
||||
geom = newpoly;
|
||||
}
|
||||
else if (geom->getDimension() == 3) {
|
||||
shared_ptr<const PolySet> ps = dynamic_pointer_cast<const PolySet>(geom);
|
||||
if (!ps) {
|
||||
shared_ptr<const CGAL_Nef_polyhedron> N = dynamic_pointer_cast<const CGAL_Nef_polyhedron>(geom);
|
||||
assert(N);
|
||||
ps.reset(N->convertToPolyset());
|
||||
if (!ps) {
|
||||
PRINT("ERROR: Failed to convert Nef polyhedron to PolySet; bend only works on PolySets. Is your object non-manifold?\n");
|
||||
}
|
||||
}
|
||||
if (ps) {
|
||||
|
||||
// If we got a const object, make a copy
|
||||
shared_ptr<PolySet> newps;
|
||||
if (res.isConst()) newps.reset(new PolySet(*ps));
|
||||
else newps = dynamic_pointer_cast<PolySet>(res.ptr());
|
||||
|
||||
// b1: vertical radius
|
||||
b1[0] = node.fixed_x-node.center_x;
|
||||
b1[1] = node.fixed_y-node.center_y;
|
||||
b1[2] = node.fixed_z-node.center_z;
|
||||
R0 = sqrt(b1[0]*b1[0] + b1[1]*b1[1] + b1[2]*b1[2]);
|
||||
if (R0 < EPS) {
|
||||
PRINT("Error: 3D bend center is equal to fixed point, skipping object.");
|
||||
return ContinueTraversal;
|
||||
}
|
||||
b1[0] = b1[0]/R0;
|
||||
b1[1] = b1[1]/R0;
|
||||
b1[2] = b1[2]/R0;
|
||||
|
||||
// b2: parallel to the axis of bend cylinder
|
||||
b2[0] = node.cyl_x-node.fixed_x;
|
||||
b2[1] = node.cyl_y-node.fixed_y;
|
||||
b2[2] = node.cyl_z-node.fixed_z;
|
||||
m = b2[0]*b1[0] + b2[1]*b1[1] + b2[2]*b1[2];
|
||||
b2[0] = b2[0] - m*b1[0];
|
||||
b2[1] = b2[1] - m*b1[1];
|
||||
b2[2] = b2[2] - m*b1[2];
|
||||
m = sqrt(b2[0]*b2[0] + b2[1]*b2[1] + b2[2]*b2[2]);
|
||||
|
||||
if (m < EPS) {
|
||||
// center, fixed and cyl are laying on a single straight line => 3D spherical bend
|
||||
BOOST_FOREACH(PolySet::Polygon &p, newps->polygons) {
|
||||
BOOST_FOREACH(Vector3d &v, p) {
|
||||
ov[0] = v[0]-node.center_x;
|
||||
ov[1] = v[1]-node.center_y;
|
||||
ov[2] = v[2]-node.center_z;
|
||||
R = ov[0]*b1[0] + ov[1]*b1[1] + ov[2]*b1[2];
|
||||
b2[0] = ov[0]-R*b1[0];
|
||||
b2[1] = ov[1]-R*b1[1];
|
||||
b2[2] = ov[2]-R*b1[2];
|
||||
L = sqrt(b2[0]*b2[0] + b2[1]*b2[1] + b2[2]*b2[2]);
|
||||
if (L > EPS) {
|
||||
b2[0] /= L;
|
||||
b2[1] /= L;
|
||||
b2[2] /= L;
|
||||
}
|
||||
a = L/R0;
|
||||
v[0] = node.center_x + R*b1[0]*cos(a) + R*b2[0]*sin(a);
|
||||
v[1] = node.center_y + R*b1[1]*cos(a) + R*b2[1]*sin(a);
|
||||
v[2] = node.center_z + R*b1[2]*cos(a) + R*b2[2]*sin(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
b2[0] = b2[0]/m;
|
||||
b2[1] = b2[1]/m;
|
||||
b2[2] = b2[2]/m;
|
||||
// b3: third basis vector - cross product of b1, b2
|
||||
b3[0] = b1[1] * b2[2] - b1[2] * b2[1];
|
||||
b3[1] = b1[2] * b2[0] - b1[0] * b2[2];
|
||||
b3[2] = b1[0] * b2[1] - b1[1] * b2[0];
|
||||
|
||||
// 3D cylindric bend
|
||||
BOOST_FOREACH(PolySet::Polygon &p, newps->polygons) {
|
||||
BOOST_FOREACH(Vector3d &v, p) {
|
||||
ov[0] = v[0]-node.center_x;
|
||||
ov[1] = v[1]-node.center_y;
|
||||
ov[2] = v[2]-node.center_z;
|
||||
R = ov[0]*b1[0] + ov[1]*b1[1] + ov[2]*b1[2];
|
||||
Y = ov[0]*b2[0] + ov[1]*b2[1] + ov[2]*b2[2];
|
||||
L = ov[0]*b3[0] + ov[1]*b3[1] + ov[2]*b3[2];
|
||||
a = L/R0;
|
||||
v[0] = node.center_x + Y*b2[0] + R*b1[0]*cos(a) + R*b3[0]*sin(a);
|
||||
v[1] = node.center_y + Y*b2[1] + R*b1[1]*cos(a) + R*b3[1]*sin(a);
|
||||
v[2] = node.center_z + Y*b2[2] + R*b1[2]*cos(a) + R*b3[2]*sin(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
geom = newps;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
geom = smartCacheGet(node);
|
||||
}
|
||||
addToParent(state, node, geom);
|
||||
}
|
||||
return ContinueTraversal;
|
||||
#undef EPS
|
||||
}
|
||||
|
||||
/*!
|
||||
input: List of 2D or 3D objects (not mixed)
|
||||
output: Polygon2d or 3D PolySet
|
||||
|
|
|
@ -21,6 +21,7 @@ public:
|
|||
virtual Response visit(State &state, const AbstractNode &node);
|
||||
virtual Response visit(State &state, const AbstractIntersectionNode &node);
|
||||
virtual Response visit(State &state, const AbstractPolyNode &node);
|
||||
virtual Response visit(State &state, const BendNode &node);
|
||||
virtual Response visit(State &state, const LinearExtrudeNode &node);
|
||||
virtual Response visit(State &state, const RotateExtrudeNode &node);
|
||||
virtual Response visit(State &state, const LeafNode &node);
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* OpenSCAD (www.openscad.org)
|
||||
* Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and
|
||||
* Marius Kintel <marius@kintel.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* As a special exception, you have permission to link this program
|
||||
* with the CGAL library and distribute executables, as long as you
|
||||
* follow the requirements of the GNU GPL in regard to all of the
|
||||
* software in the executable aside from CGAL.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "bendnode.h"
|
||||
|
||||
#include "module.h"
|
||||
#include "evalcontext.h"
|
||||
#include "printutils.h"
|
||||
#include "fileutils.h"
|
||||
#include "builtin.h"
|
||||
#include "calc.h"
|
||||
#include "polyset.h"
|
||||
#include "mathc99.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <boost/assign/std/vector.hpp>
|
||||
using namespace boost::assign; // bring 'operator+=()' into scope
|
||||
|
||||
class BendModule : public AbstractModule
|
||||
{
|
||||
public:
|
||||
BendModule() { }
|
||||
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
|
||||
};
|
||||
|
||||
AbstractNode *BendModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
|
||||
{
|
||||
BendNode *node = new BendNode(inst);
|
||||
|
||||
AssignmentList args;
|
||||
args += Assignment("center"), Assignment("fixed"), Assignment("cyl");
|
||||
|
||||
Context c(ctx);
|
||||
c.setVariables(args, evalctx);
|
||||
|
||||
Value convexity = c.lookup_variable("convexity", true);
|
||||
Value center = c.lookup_variable("center");
|
||||
Value fixed = c.lookup_variable("fixed");
|
||||
Value cyl = c.lookup_variable("cyl");
|
||||
|
||||
node->convexity = (int)convexity.toDouble();
|
||||
center.getVec3(node->center_x, node->center_y, node->center_z);
|
||||
fixed.getVec3(node->fixed_x, node->fixed_y, node->fixed_z);
|
||||
cyl.getVec3(node->cyl_x, node->cyl_y, node->cyl_z);
|
||||
|
||||
if (node->convexity <= 0)
|
||||
node->convexity = 1;
|
||||
|
||||
std::vector<AbstractNode *> instantiatednodes = inst->instantiateChildren(evalctx);
|
||||
node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end());
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
std::string BendNode::toString() const
|
||||
{
|
||||
std::stringstream stream;
|
||||
|
||||
stream << this->name()
|
||||
<< "(center = [ " << center_x << ", " << center_y << ", " << center_z << " ]"
|
||||
<< ", fixed = [ " << fixed_x << ", " << fixed_y << ", " << fixed_z << " ]"
|
||||
<< ", cyl = [ " << cyl_x << ", " << cyl_y << ", " << cyl_z << " ]"
|
||||
<< ", convexity = " << this->convexity << ")";
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
void register_builtin_bend()
|
||||
{
|
||||
Builtins::init("bend", new BendModule());
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include "node.h"
|
||||
#include "visitor.h"
|
||||
#include "value.h"
|
||||
|
||||
class BendNode: public AbstractPolyNode
|
||||
{
|
||||
public:
|
||||
BendNode(const ModuleInstantiation *mi): AbstractPolyNode(mi) {
|
||||
convexity = 0;
|
||||
center_x = center_y = center_z = 0;
|
||||
fixed_x = fixed_y = fixed_z = 0;
|
||||
is3d = false;
|
||||
}
|
||||
virtual Response accept(class State &state, Visitor &visitor) const {
|
||||
return visitor.visit(state, *this);
|
||||
}
|
||||
virtual std::string toString() const;
|
||||
virtual std::string name() const { return "bend"; }
|
||||
|
||||
int convexity;
|
||||
bool is3d;
|
||||
// center is the center of bend circle/sphere
|
||||
double center_x, center_y, center_z;
|
||||
// fixed point, i.e. point that doesn't move after transformation
|
||||
double fixed_x, fixed_y, fixed_z;
|
||||
// this point specifies the orientation of bend cylinder.
|
||||
// bend will be spherical if all 3 points (center, fixed and cyl)
|
||||
// are laying on a single straight line
|
||||
double cyl_x, cyl_y, cyl_z;
|
||||
};
|
|
@ -35,6 +35,7 @@ void Builtins::init(const char *name, class AbstractFunction *function)
|
|||
}
|
||||
|
||||
extern void register_builtin_functions();
|
||||
extern void register_builtin_bend();
|
||||
extern void register_builtin_csgops();
|
||||
extern void register_builtin_transform();
|
||||
extern void register_builtin_color();
|
||||
|
@ -76,6 +77,7 @@ void Builtins::initialize()
|
|||
register_builtin_dxf_linear_extrude();
|
||||
register_builtin_dxf_rotate_extrude();
|
||||
register_builtin_text();
|
||||
register_builtin_bend();
|
||||
|
||||
this->deprecations["dxf_linear_extrude"] = "linear_extrude()";
|
||||
this->deprecations["dxf_rotate_extrude"] = "rotate_extrude()";
|
||||
|
|
|
@ -220,7 +220,7 @@ Highlighter::Highlighter(QTextDocument *parent)
|
|||
tokentypes["operator"] << "=" << "!" << "&&" << "||" << "+" << "-" << "*" << "/" << "%" << "!" << "#" << ";";
|
||||
tokentypes["math"] << "abs" << "sign" << "acos" << "asin" << "atan" << "atan2" << "sin" << "cos" << "floor" << "round" << "ceil" << "ln" << "log" << "lookup" << "min" << "max" << "pow" << "sqrt" << "exp" << "rands";
|
||||
tokentypes["keyword"] << "module" << "function" << "for" << "intersection_for" << "if" << "assign" << "echo"<< "search" << "str" << "let";
|
||||
tokentypes["transform"] << "scale" << "translate" << "rotate" << "multmatrix" << "color" << "projection" << "hull" << "resize" << "mirror" << "minkowski";
|
||||
tokentypes["transform"] << "bend" << "scale" << "translate" << "rotate" << "multmatrix" << "color" << "projection" << "hull" << "resize" << "mirror" << "minkowski";
|
||||
tokentypes["csgop"] << "union" << "intersection" << "difference" << "render";
|
||||
tokentypes["prim3d"] << "cube" << "cylinder" << "sphere" << "polyhedron";
|
||||
tokentypes["prim2d"] << "square" << "polygon" << "circle";
|
||||
|
|
|
@ -18,6 +18,9 @@ public:
|
|||
virtual Response visit(class State &state, const class LeafNode &node) {
|
||||
return visit(state, (const class AbstractPolyNode &)node);
|
||||
}
|
||||
virtual Response visit(class State &state, const class BendNode &node) {
|
||||
return visit(state, (const class AbstractPolyNode &)node);
|
||||
}
|
||||
virtual Response visit(class State &state, const class CgaladvNode &node) {
|
||||
return visit(state, (const class AbstractNode &)node);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue