diff options
-rw-r--r-- | examples/example021.scad | 32 | ||||
-rw-r--r-- | openscad.pro | 1 | ||||
-rw-r--r-- | src/builtin.h | 1 | ||||
-rw-r--r-- | src/export.cc | 36 | ||||
-rw-r--r-- | src/export.h | 1 | ||||
-rw-r--r-- | src/module.cc | 1 | ||||
-rw-r--r-- | src/openscad.cc | 2 | ||||
-rw-r--r-- | src/projection.cc | 282 |
8 files changed, 354 insertions, 2 deletions
diff --git a/examples/example021.scad b/examples/example021.scad new file mode 100644 index 0000000..f5dfb78 --- /dev/null +++ b/examples/example021.scad @@ -0,0 +1,32 @@ + +module thing() +{ + $fa = 30; + difference() { + sphere(r = 25); + cylinder(h = 62.5, r1 = 12.5, r2 = 6.25, center = true); + rotate(90, [ 1, 0, 0 ]) cylinder(h = 62.5, + r1 = 12.5, r2 = 6.25, center = true); + rotate(90, [ 0, 1, 0 ]) cylinder(h = 62.5, + r1 = 12.5, r2 = 6.25, center = true); + } +} + +module demo_proj() +{ + linear_extrude(center = true, height = 0.5) projection(cut = false) thing(); + % thing(); +} + +module demo_cut() +{ + for (i=[-20:5:+20]) { + rotate(-30, [ 1, 1, 0 ]) translate([ 0, 0, -i ]) + linear_extrude(center = true, height = 0.5) projection(cut = true) + translate([ 0, 0, i ]) rotate(+30, [ 1, 1, 0 ]) thing(); + } + % thing(); +} + +translate([ -30, 0, 0 ]) demo_proj(); +translate([ +30, 0, 0 ]) demo_cut(); diff --git a/openscad.pro b/openscad.pro index 7546667..cb8afe7 100644 --- a/openscad.pro +++ b/openscad.pro @@ -94,6 +94,7 @@ SOURCES += src/openscad.cc \ src/csgops.cc \ src/transform.cc \ src/primitives.cc \ + src/projection.cc \ src/surface.cc \ src/control.cc \ src/render.cc \ diff --git a/src/builtin.h b/src/builtin.h index dbdd818..9156adc 100644 --- a/src/builtin.h +++ b/src/builtin.h @@ -18,6 +18,7 @@ extern void register_builtin_surface(); extern void register_builtin_control(); extern void register_builtin_render(); extern void register_builtin_import(); +extern void register_builtin_projection(); extern void register_builtin_dxf_linear_extrude(); extern void register_builtin_dxf_rotate_extrude(); extern void initialize_builtin_dxf_dim(); diff --git a/src/export.cc b/src/export.cc index 99419aa..840525c 100644 --- a/src/export.cc +++ b/src/export.cc @@ -29,6 +29,42 @@ #ifdef ENABLE_CGAL #include "cgal.h" +void cgal_nef3_to_polyset(PolySet *ps, CGAL_Nef_polyhedron *root_N) +{ + CGAL_Polyhedron P; + root_N->p3.convert_to_Polyhedron(P); + + typedef CGAL_Polyhedron::Vertex Vertex; + typedef CGAL_Polyhedron::Vertex_const_iterator VCI; + typedef CGAL_Polyhedron::Facet_const_iterator FCI; + typedef CGAL_Polyhedron::Halfedge_around_facet_const_circulator HFCC; + + for (FCI fi = P.facets_begin(); fi != P.facets_end(); ++fi) { + HFCC hc = fi->facet_begin(); + HFCC hc_end = hc; + Vertex v1, v2, v3; + v1 = *VCI((hc++)->vertex()); + v3 = *VCI((hc++)->vertex()); + do { + v2 = v3; + v3 = *VCI((hc++)->vertex()); + double x1 = CGAL::to_double(v1.point().x()); + double y1 = CGAL::to_double(v1.point().y()); + double z1 = CGAL::to_double(v1.point().z()); + double x2 = CGAL::to_double(v2.point().x()); + double y2 = CGAL::to_double(v2.point().y()); + double z2 = CGAL::to_double(v2.point().z()); + double x3 = CGAL::to_double(v3.point().x()); + double y3 = CGAL::to_double(v3.point().y()); + double z3 = CGAL::to_double(v3.point().z()); + ps->append_poly(); + ps->append_vertex(x1, y1, z1); + ps->append_vertex(x2, y2, z2); + ps->append_vertex(x3, y3, z3); + } while (hc != hc_end); + } +} + void export_stl(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd) { CGAL_Polyhedron P; diff --git a/src/export.h b/src/export.h index 49319ba..619d9e0 100644 --- a/src/export.h +++ b/src/export.h @@ -2,6 +2,7 @@ #define EXPORT_H_ #ifdef ENABLE_CGAL +void cgal_nef3_to_polyset(PolySet *ps, CGAL_Nef_polyhedron *root_N); void export_stl(class CGAL_Nef_polyhedron *root_N, QString filename, class QProgressDialog *pd); void export_off(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd); void export_dxf(CGAL_Nef_polyhedron *root_N, QString filename, QProgressDialog *pd); diff --git a/src/module.cc b/src/module.cc index 6352658..f2c6e94 100644 --- a/src/module.cc +++ b/src/module.cc @@ -194,6 +194,7 @@ void initialize_builtin_modules() register_builtin_control(); register_builtin_render(); register_builtin_import(); + register_builtin_projection(); register_builtin_dxf_linear_extrude(); register_builtin_dxf_rotate_extrude(); } diff --git a/src/openscad.cc b/src/openscad.cc index 8c0db68..09e6f8c 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -18,8 +18,6 @@ * */ -#define INCLUDE_ABSTRACT_NODE_DETAILS - #include "openscad.h" #include "MainWindow.h" #include "node.h" diff --git a/src/projection.cc b/src/projection.cc new file mode 100644 index 0000000..d2f417c --- /dev/null +++ b/src/projection.cc @@ -0,0 +1,282 @@ +/* + * OpenSCAD (www.openscad.at) + * Copyright (C) 2009 Clifford Wolf <clifford@clifford.at> + * + * 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. + * + * 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 "module.h" +#include "node.h" +#include "context.h" +#include "printutils.h" +#include "builtin.h" +#include "dxfdata.h" +#include "dxftess.h" +#include "polyset.h" +#include "export.h" +#include "openscad.h" // get_fragments_from_r() + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <assert.h> + +#include <QApplication> +#include <QTime> +#include <QProgressDialog> + +class ProjectionModule : public AbstractModule +{ +public: + ProjectionModule() { } + virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; +}; + +class ProjectionNode : public AbstractPolyNode +{ +public: + int convexity; + bool cut_mode; + ProjectionNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) { + cut_mode = false; + } + virtual PolySet *render_polyset(render_mode_e mode) const; + virtual QString dump(QString indent) const; +}; + +AbstractNode *ProjectionModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const +{ + ProjectionNode *node = new ProjectionNode(inst); + + QVector<QString> argnames = QVector<QString>() << "cut"; + QVector<Expression*> argexpr; + + Context c(ctx); + c.args(argnames, argexpr, inst->argnames, inst->argvalues); + + Value convexity = c.lookup_variable("convexity", true); + Value cut = c.lookup_variable("cut", true); + + node->convexity = (int)convexity.num; + + if (cut.type == Value::BOOL) + node->cut_mode = cut.b; + + foreach (ModuleInstantiation *v, inst->children) { + AbstractNode *n = v->evaluate(inst->ctx); + if (n) + node->children.append(n); + } + + return node; +} + +void register_builtin_projection() +{ + builtin_modules["projection"] = new ProjectionModule(); +} + +static void report_func(const class AbstractNode*, void *vp, int mark) +{ + QProgressDialog *pd = (QProgressDialog*)vp; + int v = (int)((mark*100.0) / progress_report_count); + pd->setValue(v < 100 ? v : 99); + QString label; + label.sprintf("Rendering Polygon Mesh using CGAL (%d/%d)", mark, progress_report_count); + pd->setLabelText(label); + QApplication::processEvents(); +} + +PolySet *ProjectionNode::render_polyset(render_mode_e rm) const +{ + QString key = mk_cache_id(); + if (PolySet::ps_cache.contains(key)) { + PRINT(PolySet::ps_cache[key]->msg); + return PolySet::ps_cache[key]->ps->link(); + } + + print_messages_push(); + + QTime t; + QProgressDialog *pd = NULL; + + if (rm == RENDER_OPENCSG) + { + PRINT_NOCACHE("Processing uncached projection body..."); + QApplication::processEvents(); + + t.start(); + pd = new QProgressDialog("Rendering Polygon Mesh using CGAL...", QString(), 0, 100); + pd->setValue(0); + pd->setAutoClose(false); + pd->show(); + QApplication::processEvents(); + + progress_report_prep((AbstractNode*)this, report_func, pd); + } + + CGAL_Nef_polyhedron N; + N.dim = 3; + foreach(AbstractNode *v, this->children) { + if (v->modinst->tag_background) + continue; + N.p3 += v->render_cgal_nef_polyhedron().p3; + } + + if (rm == RENDER_OPENCSG) { + progress_report_fin(); + int s = t.elapsed() / 1000; + PRINTF_NOCACHE("..rendering time: %d hours, %d minutes, %d seconds", s / (60*60), (s / 60) % 60, s % 60); + delete pd; + } + + PolySet *ps = new PolySet(); + ps->convexity = this->convexity; + ps->is2d = true; + + if (cut_mode) + { + PolySet *cube = new PolySet(); + double infval = 1e8, eps = 0.1; + double x1 = -infval, x2 = +infval, y1 = -infval, y2 = +infval, z1 = 0, z2 = eps; + + cube->append_poly(); // top + cube->append_vertex(x1, y1, z2); + cube->append_vertex(x2, y1, z2); + cube->append_vertex(x2, y2, z2); + cube->append_vertex(x1, y2, z2); + + cube->append_poly(); // bottom + cube->append_vertex(x1, y2, z1); + cube->append_vertex(x2, y2, z1); + cube->append_vertex(x2, y1, z1); + cube->append_vertex(x1, y1, z1); + + cube->append_poly(); // side1 + cube->append_vertex(x1, y1, z1); + cube->append_vertex(x2, y1, z1); + cube->append_vertex(x2, y1, z2); + cube->append_vertex(x1, y1, z2); + + cube->append_poly(); // side2 + cube->append_vertex(x2, y1, z1); + cube->append_vertex(x2, y2, z1); + cube->append_vertex(x2, y2, z2); + cube->append_vertex(x2, y1, z2); + + cube->append_poly(); // side3 + cube->append_vertex(x2, y2, z1); + cube->append_vertex(x1, y2, z1); + cube->append_vertex(x1, y2, z2); + cube->append_vertex(x2, y2, z2); + + cube->append_poly(); // side4 + cube->append_vertex(x1, y2, z1); + cube->append_vertex(x1, y1, z1); + cube->append_vertex(x1, y1, z2); + cube->append_vertex(x1, y2, z2); + CGAL_Nef_polyhedron Ncube = cube->render_cgal_nef_polyhedron(); + cube->unlink(); + + PolySet *ps3 = new PolySet(); + // N.p3 *= CGAL_Nef_polyhedron3(CGAL_Plane(0, 0, 1, 0), CGAL_Nef_polyhedron3::INCLUDED); + N.p3 *= Ncube.p3; + cgal_nef3_to_polyset(ps3, &N); + for (int i = 0; i < ps3->polygons.size(); i++) { + for (int j = 0; j < ps3->polygons[i].size(); j++) { + if (ps3->polygons[i][j].z != 0) + goto next_ps3_polygon_cut_mode; + } + ps->append_poly(); + for (int j = 0; j < ps3->polygons[i].size(); j++) { + ps->insert_vertex(ps3->polygons[i][j].x, ps3->polygons[i][j].y); + } + next_ps3_polygon_cut_mode:; + } + ps3->unlink(); + } + else + { + PolySet *ps3 = new PolySet(); + cgal_nef3_to_polyset(ps3, &N); + CGAL_Nef_polyhedron np; + np.dim = 2; + for (int i = 0; i < ps3->polygons.size(); i++) + { + int min_x_p = -1; + double min_x_val = 0; + for (int j = 0; j < ps3->polygons[i].size(); j++) { + double x = ps3->polygons[i][j].x; + if (min_x_p < 0 || x < min_x_val) { + min_x_p = j; + min_x_val = x; + } + } + int min_x_p1 = (min_x_p+1) % ps3->polygons[i].size(); + int min_x_p2 = (min_x_p+ps3->polygons[i].size()-1) % ps3->polygons[i].size(); + double ax = ps3->polygons[i][min_x_p1].x - ps3->polygons[i][min_x_p].x; + double ay = ps3->polygons[i][min_x_p1].y - ps3->polygons[i][min_x_p].y; + double at = atan2(ay, ax); + double bx = ps3->polygons[i][min_x_p2].x - ps3->polygons[i][min_x_p].x; + double by = ps3->polygons[i][min_x_p2].y - ps3->polygons[i][min_x_p].y; + double bt = atan2(by, bx); + + double eps = 0.000001; + if (fabs(at - bt) < eps || (fabs(ax) < eps && fabs(ay) < eps) || + (fabs(bx) < eps && fabs(by) < eps)) { + // this triangle is degenerated in projection + continue; + } + + std::list<CGAL_Nef_polyhedron2::Point> plist; + for (int j = 0; j < ps3->polygons[i].size(); j++) { + double x = ps3->polygons[i][j].x; + double y = ps3->polygons[i][j].y; + CGAL_Nef_polyhedron2::Point p = CGAL_Nef_polyhedron2::Point(x, y); + if (at > bt) + plist.push_front(p); + else + plist.push_back(p); + } + np.p2 += CGAL_Nef_polyhedron2(plist.begin(), plist.end(), + CGAL_Nef_polyhedron2::INCLUDED); + } + DxfData dxf(np); + dxf_tesselate(ps, &dxf, 0, true, false, 0); + dxf_border_to_ps(ps, &dxf); + ps3->unlink(); + } + + PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link())); + print_messages_pop(); + + return ps; +} + +QString ProjectionNode::dump(QString indent) const +{ + if (dump_cache.isEmpty()) { + QString text; + text.sprintf("projection(cut = %s, convexity = %d) {\n", + this->cut_mode ? "true" : "false", this->convexity); + foreach (AbstractNode *v, this->children) + text += v->dump(indent + QString("\t")); + text += indent + "}\n"; + ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; + } + return dump_cache; +} + |