BlinkenArea - GitList
Repositories
Blog
Wiki
dxfngc
Code
Commits
Branches
Tags
Search
Tree:
1030916
Branches
Tags
master
dxfngc
src
traverse.cpp
license CC-BY-SA --> GPL (more suitable for source code)
Stefan Schuermans
commited
1030916
at 2013-05-27 17:35:02
traverse.cpp
Blame
History
Raw
/* drawing (DXF) to G-code (NGC) converter * Copyright 2013 Stefan Schuermans <stefan@schuermans.info> * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html */ #include <dime/entities/Circle.h> #include <dime/entities/Entity.h> #include <dime/State.h> #include <dime/util/Linear.h> #include <iostream> #include <math.h> #include <string> #include "drawing.h" #include "layer.h" #include "path.h" #include "point.h" #include "traverse.h" /// minimum number of segments to approximate circle with #define TRAVERSE_CIRCLE_NUM_SEG_MIN (16) /// maximum number of segments to approximate circle with #define TRAVERSE_CIRCLE_NUM_SEG_MAX (4096) /** * @brief process an entity during DXF traversal * @param[in] state current state of libdime * @param[in] entity pointer to entity * @param[in] vp_ctx context pointer, must point to traverse_ctx structure * @return if to continue enumeration */ bool traverse_entity(const class dimeState *const state, dimeEntity *entity, void *vp_ctx) { struct traverse_ctx *p_ctx = (struct traverse_ctx *)vp_ctx; // get layer std::string strLayerName = entity->getLayerName(); Layer &layer = p_ctx->mpDrawing->mLayers[strLayerName]; // get transformation matrix dimeMatrix matrix; state->getMatrix(matrix); /* special handling for some entities to avoid libdime's inexact conversion to lines */ dimeArc *arc; dimeCircle *circle; dimeEllipse *ellipse; if ((arc = dynamic_cast<dimeArc *>(entity))) traverse_arc(arc, matrix, p_ctx->precision, layer); else if ((circle = dynamic_cast<dimeCircle *>(entity))) traverse_circle(circle, matrix, p_ctx->precision, layer); else if ((ellipse = dynamic_cast<dimeEllipse *>(entity))) traverse_ellipse(ellipse, matrix, p_ctx->precision, layer); else traverse_generic(entity, matrix, layer); // default: libdime conversion return true; // continue enumeration } /** * @brief process a generic geometric object during DXF traversal * @param[in] entity pointer to entity * @param[in] matrix current DXF transformation matrix * @param[in] layer layer to add the entitiy to */ void traverse_generic(dimeEntity *entity, const dimeMatrix &matrix, Layer &layer) { // get geometry data dimeArray<dimeVec3f> vertices; dimeArray<int> indices; dimeVec3f extrusionDir; dxfdouble thickness; entity->extractGeometry(vertices, indices, extrusionDir, thickness); // transform geometry data for (int v = 0; v < vertices.count(); ++v) matrix.multMatrixVec(vertices[v]); // no indices -> entire vertices array forms a path if (indices.count() == 0) { Path &path = layer.addPath(); for (int v = 0; v < vertices.count(); ++v) path.addPoint(vertices[v].x, vertices[v].y); } // indices available: paths given by indices else { Path *path = &layer.addPath(); for (int i = 0; i < indices.count(); ++i) { int v = indices[i]; // valid index: add to path if (v >= 0 || v < vertices.count()) path->addPoint(vertices[v].x, vertices[v].y); // invalid index: start new path else path = &layer.addPath(); } } } /** * @brief process an arc during DXF traversal * @param[in] circle pointer to arc entity * @param[in] matrix current DXF transformation matrix * @param[in] precision precision for conversion to lines * @param[in] layer layer to add the entitiy to */ void traverse_arc(dimeArc *arc, const dimeMatrix &matrix, double precision, Layer &layer) { // get parameters (not yet transformed) dimeVec3f center; arc->getCenter(center); double radius = arc->getRadius(); double start = arc->getStartAngle() * M_PI / 180.0; double end = arc->getEndAngle() * M_PI / 180.0; // add arc to layer traverse_add_circle(center, radius, start, end, precision, matrix, layer); } /** * @brief process a circle during DXF traversal * @param[in] circle pointer to circle entity * @param[in] matrix current DXF transformation matrix * @param[in] precision precision for conversion to lines * @param[in] layer layer to add the entitiy to */ void traverse_circle(dimeCircle *circle, const dimeMatrix &matrix, double precision, Layer &layer) { // get parameters and transform vertices dimeVec3f center = circle->getCenter(); double radius = circle->getRadius(); // add circle to layer traverse_add_circle(center, radius, 0.0, 2.0 * M_PI, precision, matrix, layer); } /** * @brief process an ellipse during DXF traversal * @param[in] circle pointer to ellipse entity * @param[in] matrix current DXF transformation matrix * @param[in] precision precision for conversion to lines * @param[in] layer layer to add the entitiy to */ void traverse_ellipse(dimeEllipse *ellipse, const dimeMatrix &matrix, double precision, Layer &layer) { // get parameters (not yet transformed) dimeVec3f center = ellipse->getCenter(); dimeVec3f majorEnd = ellipse->getMajorAxisEndpoint(); double ratio = ellipse->getMinorMajorRatio(); double start = ellipse->getStartParam(); double end = ellipse->getEndParam(); // add circle to layer traverse_add_ellipse(center, majorEnd, ratio, start, end, precision, matrix, layer); } /** * @brief add a circle to the layer * @param[in] center center point * @param[in] radius radius * @param[in] start start angle in radians * @param[in] end end angle in radians * @param[in] precision precision for conversion to lines * @param[in] matrix current DXF transformation matrix * @param[in] layer layer to add the entitiy to */ void traverse_add_circle(const dimeVec3f ¢er, double radius, double start, double end, double precision, const dimeMatrix &matrix, Layer &layer) { // calculate number of segments to approximate part of circle with unsigned int num_seg = traverse_calc_num_seg(radius, start, end, precision); // loop over all angles to calculate point for if (end <= start) end += 2.0 * M_PI; unsigned int seg; Path &path = layer.addPath(); for (seg = 0; seg <= num_seg; ++seg) { double angle = (start * (num_seg - seg) + end * seg) / num_seg; // get point double x = center.x + radius * cos(angle); double y = center.y + radius * sin(angle); // transform point via current matrix and add it to path dimeVec3f vertex(x, y, 0.0); matrix.multMatrixVec(vertex); path.addPoint(vertex.x, vertex.y); } // for seg } /** * @brief add ellipse to the layer * @param[in] center center point * @param[in] majorEnd endpoint of major axis * @param[in] ratio ratio of minor to major axis * @param[in] start start angle in radians * @param[in] end end angle in radians * @param[in] precision precision for conversion to lines * @param[in] matrix current DXF transformation matrix * @param[in] layer layer to add the entitiy to */ void traverse_add_ellipse(const dimeVec3f ¢er, const dimeVec3f &majorEnd, double ratio, double start, double end, double precision, const dimeMatrix &matrix, Layer &layer) { // calculate number of segments to approximate part of ellipse with double majorRadius = (majorEnd - center).length(); double maxRadius = ratio <= 1.0 ? majorRadius : majorRadius * ratio; unsigned int num_seg = traverse_calc_num_seg(maxRadius, start, end, precision); // build transformation matrix: union circle -> ellipse double maj_x = majorEnd.x; double maj_y = majorEnd.y; double min_x = -ratio * maj_y; double min_y = ratio * maj_x; // loop over all angles to calculate point for if (end <= start) end += 2.0 * M_PI; unsigned int seg; Path &path = layer.addPath(); for (seg = 0; seg <= num_seg; ++seg) { double angle = (start * (num_seg - seg) + end * seg) / num_seg; // get point on union circle double uc_x = cos(angle); double uc_y = sin(angle); // transformation: union circle -> ellipse double x = maj_x * uc_x + min_x * uc_y; double y = maj_y * uc_x + min_y * uc_y; // translate: move to center x += center.x; y += center.y; // transform point via current matrix and add it to path dimeVec3f vertex(x, y, 0.0); matrix.multMatrixVec(vertex); path.addPoint(vertex.x, vertex.y); } // for seg } /** * @brief calculate number of segments to approx. part of circle/ellipse with * @param[in] maxRadius radius of circle / maximum radius of ellipse * @param[in] start start angle in radians * @param[in] end end angle in radians * @param[in] precision precision for conversion to lines * @return number of segments to approximate circle/ellipse with */ unsigned int traverse_calc_num_seg(double maxRadius, double start, double end, double precision) { // calculate number of segments for full circle/ellipse unsigned int num_seg; double dist; // double number of segments until precision is met (or maximum reached) for (num_seg = TRAVERSE_CIRCLE_NUM_SEG_MIN; num_seg < TRAVERSE_CIRCLE_NUM_SEG_MAX; num_seg *= 2) { // maximum distance of circle and regular polygon with num_seg sides dist = maxRadius * (1.0 - cos(M_PI / num_seg)); // precision met -> done if (dist < precision) break; } // calculate number of segments for part of cirlce/ellipse double angle = start < end ? end - start : 2.0 * M_PI + end - start; num_seg = (unsigned int)ceil((double)num_seg * angle / (2.0 * M_PI)); return num_seg; }