diff options
Diffstat (limited to 'src/primitives.cc')
-rw-r--r-- | src/primitives.cc | 529 |
1 files changed, 529 insertions, 0 deletions
diff --git a/src/primitives.cc b/src/primitives.cc new file mode 100644 index 0000000..2f50578 --- /dev/null +++ b/src/primitives.cc @@ -0,0 +1,529 @@ +/* + * 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 + * + */ + +#define INCLUDE_ABSTRACT_NODE_DETAILS + +#include "openscad.h" + +enum primitive_type_e { + CUBE, + SPHERE, + CYLINDER, + POLYHEDRON, + SQUARE, + CIRCLE, + POLYGON +}; + +class PrimitiveModule : public AbstractModule +{ +public: + primitive_type_e type; + PrimitiveModule(primitive_type_e type) : type(type) { } + virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const; +}; + +class PrimitiveNode : public AbstractPolyNode +{ +public: + bool center; + double x, y, z, h, r1, r2; + double fn, fs, fa; + primitive_type_e type; + int convexity; + Value points, paths, triangles; + PrimitiveNode(const ModuleInstantiation *mi, primitive_type_e type) : AbstractPolyNode(mi), type(type) { } + virtual PolySet *render_polyset(render_mode_e mode) const; + virtual QString dump(QString indent) const; +}; + +AbstractNode *PrimitiveModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const +{ + PrimitiveNode *node = new PrimitiveNode(inst, type); + + node->center = false; + node->x = node->y = node->z = node->h = node->r1 = node->r2 = 1; + + QVector<QString> argnames; + QVector<Expression*> argexpr; + + if (type == CUBE) { + argnames = QVector<QString>() << "size" << "center"; + } + if (type == SPHERE) { + argnames = QVector<QString>() << "r"; + } + if (type == CYLINDER) { + argnames = QVector<QString>() << "h" << "r1" << "r2" << "center"; + } + if (type == POLYHEDRON) { + argnames = QVector<QString>() << "points" << "triangles" << "convexity"; + } + if (type == SQUARE) { + argnames = QVector<QString>() << "size" << "center"; + } + if (type == CIRCLE) { + argnames = QVector<QString>() << "r"; + } + if (type == POLYGON) { + argnames = QVector<QString>() << "points" << "paths" << "convexity"; + } + + Context c(ctx); + c.args(argnames, argexpr, inst->argnames, inst->argvalues); + + node->fn = c.lookup_variable("$fn").num; + node->fs = c.lookup_variable("$fs").num; + node->fa = c.lookup_variable("$fa").num; + + if (type == CUBE) { + Value size = c.lookup_variable("size"); + Value center = c.lookup_variable("center"); + size.getnum(node->x); + size.getnum(node->y); + size.getnum(node->z); + size.getv3(node->x, node->y, node->z); + if (center.type == Value::BOOL) { + node->center = center.b; + } + } + + if (type == SPHERE) { + Value r = c.lookup_variable("r"); + if (r.type == Value::NUMBER) { + node->r1 = r.num; + } + } + + if (type == CYLINDER) { + Value h = c.lookup_variable("h"); + Value r, r1, r2; + r1 = c.lookup_variable("r1"); + r2 = c.lookup_variable("r2"); + if (r1.type != Value::NUMBER && r1.type != Value::NUMBER) + r = c.lookup_variable("r"); + Value center = c.lookup_variable("center"); + if (h.type == Value::NUMBER) { + node->h = h.num; + } + if (r.type == Value::NUMBER) { + node->r1 = r.num; + node->r2 = r.num; + } + if (r1.type == Value::NUMBER) { + node->r1 = r1.num; + } + if (r2.type == Value::NUMBER) { + node->r2 = r2.num; + } + if (center.type == Value::BOOL) { + node->center = center.b; + } + } + + if (type == POLYHEDRON) { + node->points = c.lookup_variable("points"); + node->triangles = c.lookup_variable("triangles"); + } + + if (type == SQUARE) { + Value size = c.lookup_variable("size"); + Value center = c.lookup_variable("center"); + size.getnum(node->x); + size.getnum(node->y); + size.getv2(node->x, node->y); + if (center.type == Value::BOOL) { + node->center = center.b; + } + } + + if (type == CIRCLE) { + Value r = c.lookup_variable("r"); + if (r.type == Value::NUMBER) { + node->r1 = r.num; + } + } + + if (type == POLYGON) { + node->points = c.lookup_variable("points"); + node->paths = c.lookup_variable("paths"); + } + + node->convexity = c.lookup_variable("convexity", true).num; + if (node->convexity < 1) + node->convexity = 1; + + return node; +} + +void register_builtin_primitives() +{ + builtin_modules["cube"] = new PrimitiveModule(CUBE); + builtin_modules["sphere"] = new PrimitiveModule(SPHERE); + builtin_modules["cylinder"] = new PrimitiveModule(CYLINDER); + builtin_modules["polyhedron"] = new PrimitiveModule(POLYHEDRON); + builtin_modules["square"] = new PrimitiveModule(SQUARE); + builtin_modules["circle"] = new PrimitiveModule(CIRCLE); + builtin_modules["polygon"] = new PrimitiveModule(POLYGON); +} + +/*! + Returns the number of subdivision of a whole circle, given radius and + the three special variables $fn, $fs and $fa +*/ +int get_fragments_from_r(double r, double fn, double fs, double fa) +{ + if (fn > 0.0) + return (int)fn; + return (int)ceil(fmax(fmin(360.0 / fa, r*M_PI / fs), 5)); +} + +PolySet *PrimitiveNode::render_polyset(render_mode_e) const +{ + PolySet *p = new PolySet(); + + if (type == CUBE && x > 0 && y > 0 && z > 0) + { + double x1, x2, y1, y2, z1, z2; + if (center) { + x1 = -x/2; + x2 = +x/2; + y1 = -y/2; + y2 = +y/2; + z1 = -z/2; + z2 = +z/2; + } else { + x1 = y1 = z1 = 0; + x2 = x; + y2 = y; + z2 = z; + } + + p->append_poly(); // top + p->append_vertex(x1, y1, z2); + p->append_vertex(x2, y1, z2); + p->append_vertex(x2, y2, z2); + p->append_vertex(x1, y2, z2); + + p->append_poly(); // bottom + p->append_vertex(x1, y2, z1); + p->append_vertex(x2, y2, z1); + p->append_vertex(x2, y1, z1); + p->append_vertex(x1, y1, z1); + + p->append_poly(); // side1 + p->append_vertex(x1, y1, z1); + p->append_vertex(x2, y1, z1); + p->append_vertex(x2, y1, z2); + p->append_vertex(x1, y1, z2); + + p->append_poly(); // side2 + p->append_vertex(x2, y1, z1); + p->append_vertex(x2, y2, z1); + p->append_vertex(x2, y2, z2); + p->append_vertex(x2, y1, z2); + + p->append_poly(); // side3 + p->append_vertex(x2, y2, z1); + p->append_vertex(x1, y2, z1); + p->append_vertex(x1, y2, z2); + p->append_vertex(x2, y2, z2); + + p->append_poly(); // side4 + p->append_vertex(x1, y2, z1); + p->append_vertex(x1, y1, z1); + p->append_vertex(x1, y1, z2); + p->append_vertex(x1, y2, z2); + } + + if (type == SPHERE && r1 > 0) + { + struct point2d { + double x, y; + }; + + struct ring_s { + int fragments; + point2d *points; + double r, z; + }; + + int rings = get_fragments_from_r(r1, fn, fs, fa); + ring_s ring[rings]; + + for (int i = 0; i < rings; i++) { + double phi = (M_PI * (i + 0.5)) / rings; + ring[i].r = r1 * sin(phi); + ring[i].z = r1 * cos(phi); + ring[i].fragments = get_fragments_from_r(ring[i].r, fn, fs, fa); + ring[i].points = new point2d[ring[i].fragments]; + for (int j = 0; j < ring[i].fragments; j++) { + phi = (M_PI*2*j) / ring[i].fragments; + ring[i].points[j].x = ring[i].r * cos(phi); + ring[i].points[j].y = ring[i].r * sin(phi); + } + } + + p->append_poly(); + for (int i = 0; i < ring[0].fragments; i++) + p->append_vertex(ring[0].points[i].x, ring[0].points[i].y, ring[0].z); + + for (int i = 0; i < rings-1; i++) { + ring_s *r1 = &ring[i]; + ring_s *r2 = &ring[i+1]; + int r1i = 0, r2i = 0; + while (r1i < r1->fragments || r2i < r2->fragments) + { + if (r1i >= r1->fragments) + goto sphere_next_r2; + if (r2i >= r2->fragments) + goto sphere_next_r1; + if ((double)r1i / r1->fragments < + (double)r2i / r2->fragments) + { +sphere_next_r1: + p->append_poly(); + int r1j = (r1i+1) % r1->fragments; + p->insert_vertex(r1->points[r1i].x, r1->points[r1i].y, r1->z); + p->insert_vertex(r1->points[r1j].x, r1->points[r1j].y, r1->z); + p->insert_vertex(r2->points[r2i % r2->fragments].x, r2->points[r2i % r2->fragments].y, r2->z); + r1i++; + } else { +sphere_next_r2: + p->append_poly(); + int r2j = (r2i+1) % r2->fragments; + p->append_vertex(r2->points[r2i].x, r2->points[r2i].y, r2->z); + p->append_vertex(r2->points[r2j].x, r2->points[r2j].y, r2->z); + p->append_vertex(r1->points[r1i % r1->fragments].x, r1->points[r1i % r1->fragments].y, r1->z); + r2i++; + } + } + } + + p->append_poly(); + for (int i = 0; i < ring[rings-1].fragments; i++) + p->insert_vertex(ring[rings-1].points[i].x, ring[rings-1].points[i].y, ring[rings-1].z); + } + + if (type == CYLINDER && h > 0 && r1 >=0 && r2 >= 0 && (r1 > 0 || r2 > 0)) + { + int fragments = get_fragments_from_r(fmax(r1, r2), fn, fs, fa); + + double z1, z2; + if (center) { + z1 = -h/2; + z2 = +h/2; + } else { + z1 = 0; + z2 = h; + } + + struct point2d { + double x, y; + }; + + point2d circle1[fragments]; + point2d circle2[fragments]; + + for (int i=0; i<fragments; i++) { + double phi = (M_PI*2*i) / fragments; + if (r1 > 0) { + circle1[i].x = r1*cos(phi); + circle1[i].y = r1*sin(phi); + } else { + circle1[i].x = 0; + circle1[i].y = 0; + } + if (r2 > 0) { + circle2[i].x = r2*cos(phi); + circle2[i].y = r2*sin(phi); + } else { + circle2[i].x = 0; + circle2[i].y = 0; + } + } + + for (int i=0; i<fragments; i++) { + int j = (i+1) % fragments; + if (r1 > 0) { + p->append_poly(); + p->insert_vertex(circle1[i].x, circle1[i].y, z1); + p->insert_vertex(circle2[i].x, circle2[i].y, z2); + p->insert_vertex(circle1[j].x, circle1[j].y, z1); + } + if (r2 > 0) { + p->append_poly(); + p->insert_vertex(circle2[i].x, circle2[i].y, z2); + p->insert_vertex(circle2[j].x, circle2[j].y, z2); + p->insert_vertex(circle1[j].x, circle1[j].y, z1); + } + } + + if (r1 > 0) { + p->append_poly(); + for (int i=0; i<fragments; i++) + p->insert_vertex(circle1[i].x, circle1[i].y, z1); + } + + if (r2 > 0) { + p->append_poly(); + for (int i=0; i<fragments; i++) + p->append_vertex(circle2[i].x, circle2[i].y, z2); + } + } + + if (type == POLYHEDRON) + { + p->convexity = convexity; + for (int i=0; i<triangles.vec.size(); i++) + { + p->append_poly(); + for (int j=0; j<triangles.vec[i]->vec.size(); j++) { + int pt = triangles.vec[i]->vec[j]->num; + if (pt < points.vec.size()) { + double px, py, pz; + if (points.vec[pt]->getv3(px, py, pz)) + p->insert_vertex(px, py, pz); + } + } + } + } + + if (type == SQUARE) + { + double x1, x2, y1, y2; + if (center) { + x1 = -x/2; + x2 = +x/2; + y1 = -y/2; + y2 = +y/2; + } else { + x1 = y1 = 0; + x2 = x; + y2 = y; + } + + p->is2d = true; + p->append_poly(); + p->append_vertex(x1, y1); + p->append_vertex(x2, y1); + p->append_vertex(x2, y2); + p->append_vertex(x1, y2); + } + + if (type == CIRCLE) + { + int fragments = get_fragments_from_r(r1, fn, fs, fa); + + struct point2d { + double x, y; + }; + + point2d circle[fragments]; + + for (int i=0; i<fragments; i++) { + double phi = (M_PI*2*i) / fragments; + circle[i].x = r1*cos(phi); + circle[i].y = r1*sin(phi); + } + + p->is2d = true; + p->append_poly(); + for (int i=0; i<fragments; i++) + p->append_vertex(circle[i].x, circle[i].y); + } + + if (type == POLYGON) + { + DxfData dd; + + for (int i=0; i<points.vec.size(); i++) { + double x = points.vec[i]->vec[0]->num; + double y = points.vec[i]->vec[1]->num; + dd.points.append(DxfData::Point(x, y)); + } + + if (paths.vec.size() == 0) + { + dd.paths.append(DxfData::Path()); + for (int i=0; i<points.vec.size(); i++) { + if (i < dd.points.size()) { + DxfData::Point *p = &dd.points[i]; + dd.paths.last().points.append(p); + } + } + if (dd.paths.last().points.size() > 0) { + dd.paths.last().points.append(dd.paths.last().points.first()); + dd.paths.last().is_closed = true; + } + } + else + { + for (int i=0; i<paths.vec.size(); i++) + { + dd.paths.append(DxfData::Path()); + for (int j=0; j<paths.vec[i]->vec.size(); j++) { + int idx = paths.vec[i]->vec[j]->num; + if (idx < dd.points.size()) { + DxfData::Point *p = &dd.points[idx]; + dd.paths.last().points.append(p); + } + } + if (dd.paths.last().points.isEmpty()) { + dd.paths.removeLast(); + } else { + dd.paths.last().points.append(dd.paths.last().points.first()); + dd.paths.last().is_closed = true; + } + } + } + + p->is2d = true; + p->convexity = convexity; + dxf_tesselate(p, &dd, 0, true, false, 0); + dxf_border_to_ps(p, &dd); + } + + return p; +} + +QString PrimitiveNode::dump(QString indent) const +{ + if (dump_cache.isEmpty()) { + QString text; + if (type == CUBE) + text.sprintf("cube(size = [%g, %g, %g], center = %s);\n", x, y, z, center ? "true" : "false"); + if (type == SPHERE) + text.sprintf("sphere($fn = %g, $fa = %g, $fs = %g, r = %g);\n", fn, fa, fs, r1); + if (type == CYLINDER) + text.sprintf("cylinder($fn = %g, $fa = %g, $fs = %g, h = %g, r1 = %g, r2 = %g, center = %s);\n", fn, fa, fs, h, r1, r2, center ? "true" : "false"); + if (type == POLYHEDRON) + text.sprintf("polyhedron(points = %s, triangles = %s, convexity = %d);\n", points.dump().toAscii().data(), triangles.dump().toAscii().data(), convexity); + if (type == SQUARE) + text.sprintf("square(size = [%g, %g], center = %s);\n", x, y, center ? "true" : "false"); + if (type == CIRCLE) + text.sprintf("circle($fn = %g, $fa = %g, $fs = %g, r = %g);\n", fn, fa, fs, r1); + if (type == POLYGON) + text.sprintf("polygon(points = %s, paths = %s, convexity = %d);\n", points.dump().toAscii().data(), paths.dump().toAscii().data(), convexity); + ((AbstractNode*)this)->dump_cache = indent + QString("n%1: ").arg(idx) + text; + } + return dump_cache; +} + |