summaryrefslogtreecommitdiff
path: root/src/primitives.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/primitives.cc')
-rw-r--r--src/primitives.cc529
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;
+}
+
contact: Jan Huwald // Impressum