Experimental circular/spherical/cylindric bend modifier (issue openscad/openscad#815)

master
Vitaliy Filippov 2014-06-25 12:55:21 +04:00
parent 045a3b93b0
commit 80abdc1da0
8 changed files with 294 additions and 1 deletions

View File

@ -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 \

View File

@ -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

View File

@ -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);

94
src/bend.cc Normal file
View File

@ -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());
}

32
src/bendnode.h Normal file
View File

@ -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;
};

View File

@ -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()";

View File

@ -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";

View File

@ -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);
}