void GerberImporter::linear_draw_rectangular_aperture(point_type startpoint, point_type endpoint, coordinate_type width, coordinate_type height, ring_type& ring) { if (startpoint.y() > endpoint.y()) swap(startpoint, endpoint); if (startpoint.x() > endpoint.x()) { ring.push_back(point_type(startpoint.x() + width / 2, startpoint.y() + height / 2)); ring.push_back(point_type(startpoint.x() + width / 2, startpoint.y() - height / 2)); ring.push_back(point_type(startpoint.x() - width / 2, startpoint.y() - height / 2)); ring.push_back(point_type(endpoint.x() - width / 2, endpoint.y() - height / 2)); ring.push_back(point_type(endpoint.x() - width / 2, endpoint.y() + height / 2)); ring.push_back(point_type(endpoint.x() + width / 2, endpoint.y() + height / 2)); } else { ring.push_back(point_type(startpoint.x() + width / 2, startpoint.y() - height / 2)); ring.push_back(point_type(startpoint.x() - width / 2, startpoint.y() - height / 2)); ring.push_back(point_type(startpoint.x() - width / 2, startpoint.y() + height / 2)); ring.push_back(point_type(endpoint.x() - width / 2, endpoint.y() + height / 2)); ring.push_back(point_type(endpoint.x() + width / 2, endpoint.y() + height / 2)); ring.push_back(point_type(endpoint.x() + width / 2, endpoint.y() - height / 2)); } bg::correct(ring); }
void GerberImporter::linear_draw_circular_aperture(point_type startpoint, point_type endpoint, coordinate_type radius, unsigned int circle_points, ring_type& ring) { const coordinate_type dx = endpoint.x() - startpoint.x(); const coordinate_type dy = endpoint.y() - startpoint.y(); double angle_step; double offset; if (circle_points % 2 == 0) ++circle_points; if (startpoint.x() > endpoint.x()) swap(startpoint, endpoint); angle_step = 2 * bg::math::pi<double>() / circle_points; if (dx == 0) offset = bg::math::pi<double>(); else offset = atan(dy / dx) + bg::math::pi<double>() / 2; for (unsigned int i = 0; i < circle_points / 2 + 1; i++) ring.push_back(point_type(cos(angle_step * i + offset) * radius + startpoint.x(), sin(angle_step * i + offset) * radius + startpoint.y())); offset += bg::math::pi<double>(); for (unsigned int i = 0; i < circle_points / 2 + 1; i++) ring.push_back(point_type(cos(angle_step * i + offset) * radius + endpoint.x(), sin(angle_step * i + offset) * radius + endpoint.y())); bg::correct(ring); }
void GerberImporter::draw_rectangle(point_type point1, point_type point2, coordinate_type height, polygon_type& polygon) { const double angle = atan2(point2.y() - point1.y(), point2.x() - point1.x()); const coordinate_type dx = height / 2 * sin(angle); const coordinate_type dy = height / 2 * cos(angle); polygon.outer().push_back(point_type(point1.x() + dx, point1.y() - dy)); polygon.outer().push_back(point_type(point1.x() - dx, point1.y() + dy)); polygon.outer().push_back(point_type(point2.x() - dx, point2.y() + dy)); polygon.outer().push_back(point_type(point2.x() + dx, point2.y() - dy)); polygon.outer().push_back(polygon.outer().front()); bg::correct(polygon); }
void GerberImporter::circular_arc(point_type center, coordinate_type radius, double angle1, double angle2, unsigned int circle_points, linestring_type& linestring) { const unsigned int steps = ceil((angle2 - angle1) / (2 * bg::math::pi<double>()) * circle_points); const double angle_step = (angle2 - angle1) / steps; for (unsigned int i = 0; i < steps; i++) { const double angle = angle1 + i * angle_step; linestring.push_back(point_type(cos(angle) * radius + center.x(), sin(angle) * radius + center.y())); } linestring.push_back(point_type(cos(angle2) * radius + center.x(), sin(angle2) * radius + center.y())); }
void GerberImporter::draw_rectangle(point_type center, coordinate_type width, coordinate_type height, coordinate_type hole_diameter, unsigned int circle_points, polygon_type& polygon) { const coordinate_type x = center.x(); const coordinate_type y = center.y(); polygon.outer().push_back(point_type(x - width / 2, y - height / 2)); polygon.outer().push_back(point_type(x - width / 2, y + height / 2)); polygon.outer().push_back(point_type(x + width / 2, y + height / 2)); polygon.outer().push_back(point_type(x + width / 2, y - height / 2)); polygon.outer().push_back(polygon.outer().front()); if (hole_diameter != 0) { polygon.inners().resize(1); draw_regular_polygon(center, hole_diameter, circle_points, 0, false, polygon.inners().front()); } }
void GerberImporter::draw_regular_polygon(point_type center, coordinate_type diameter, unsigned int vertices, coordinate_type offset, bool clockwise, ring_type& ring) { double angle_step; if (clockwise) angle_step = -2 * bg::math::pi<double>() / vertices; else angle_step = 2 * bg::math::pi<double>() / vertices; offset *= bg::math::pi<double>() / 180.0; for (unsigned int i = 0; i < vertices; i++) ring.push_back(point_type(cos(angle_step * i + offset) * diameter / 2 + center.x(), sin(angle_step * i + offset) * diameter / 2 + center.y())); ring.push_back(ring.front()); }
shared_ptr<multi_polygon_type> Voronoi::build_voronoi(const multi_polygon_type& input, coordinate_type bounding_box_offset, coordinate_type max_dist) { auto output = make_shared<multi_polygon_type>(); voronoi_diagram_type voronoi_diagram; voronoi_builder_type voronoi_builder; vector<segment_type_p> segments; list<const cell_type *> visited_cells; size_t segments_num = 0; ring_type bounding_box_ring; bg::assign(bounding_box_ring, bg::return_buffer<box_type>( bg::return_envelope<box_type>(input), bounding_box_offset)); for (const polygon_type& polygon : input) { segments_num += polygon.outer().size() - 1; for (const ring_type& ring : polygon.inners()) { segments_num += ring.size() - 1; } } segments_num += bounding_box_ring.size() - 1; segments.reserve(segments_num); for (const polygon_type& polygon : input) { copy_ring(polygon.outer(), segments); for (const ring_type& ring : polygon.inners()) { copy_ring(ring, segments); } } copy_ring(bounding_box_ring, segments); output->resize(input.size()); for (size_t i = 0; i < input.size(); i++) (*output)[i].inners().resize(input.at(i).inners().size()); boost::polygon::insert(segments.begin(), segments.end(), &voronoi_builder); voronoi_builder.construct(&voronoi_diagram); for (const cell_type& cell : voronoi_diagram.cells()) { if (!cell.is_degenerate()) { const edge_type *edge = cell.incident_edge(); const cell_type *last_cell = NULL; const auto found_cell = std::find(visited_cells.begin(), visited_cells.end(), &cell); bool backwards = false; if (found_cell == visited_cells.end()) { pair<const polygon_type *, ring_type *> related_geometries = find_ring(input, cell, *output); if (related_geometries.first && related_geometries.second) { const polygon_type& polygon = *(related_geometries.first); ring_type& ring = *(related_geometries.second); do { if (edge->is_primary()) { if (edge->is_finite()) { const point_type startpoint (edge->vertex0()->x(), edge->vertex0()->y()); const point_type endpoint (edge->vertex1()->x(), edge->vertex1()->y()); auto append_remove_extra = [&] (const point_type& point) { /* * This works, but it seems more a workaround than a proper solution. * Why are these segments here in the first place? FIXME */ if (ring.size() >= 2 && bg::equals(point, *(ring.end() - 2))) ring.pop_back(); else ring.push_back(point); }; if (bg::covered_by(startpoint, polygon) && bg::covered_by(endpoint, polygon)) { ring.clear(); break; } if (ring.empty() || startpoint.x() != ring.back().x() || startpoint.y() != ring.back().y()) append_remove_extra(startpoint); if (edge->is_linear()) append_remove_extra(endpoint); else { vector<point_type_fp_p> sampled_edge; sample_curved_edge(edge, segments, sampled_edge, max_dist); for (auto iterator = sampled_edge.begin() + 1; iterator != sampled_edge.end(); iterator++) append_remove_extra(point_type(iterator->x(), iterator->y())); } } else { ring.clear(); break; } } else { if (!backwards) { const cell_type *current_cell = edge->cell(); const cell_type *next_cell = edge->twin()->cell(); if (next_cell == last_cell) backwards = true; else if (current_cell != &cell) visited_cells.push_front(current_cell); last_cell = current_cell; } edge = edge->twin(); } edge = edge->next(); } while (edge != cell.incident_edge()); } } else visited_cells.erase(found_cell); } } bg::correct(*output); return output; }
static inline void set(point_type& p, CoordinateType const& value) { p.y(value); }
static inline CoordinateType get(point_type const& p) { return p.y(); }
bool operator()(const point_type& a, const point_type& b) const { return std::tie(a.x(), a.y()) < std::tie(b.x(), b.y()); }
void GerberImporter::draw_oval(point_type center, coordinate_type width, coordinate_type height, coordinate_type hole_diameter, unsigned int circle_points, polygon_type& polygon) { double angle_step; double offset; coordinate_type x; coordinate_type y; coordinate_type radius; if (circle_points % 2 == 0) ++circle_points; angle_step = -2 * bg::math::pi<double>() / circle_points; if (width < height) { radius = width / 2; offset = 0; x = 0; y = height / 2 - radius; } else { radius = height / 2; offset = -bg::math::pi<double>() / 2; x = width / 2 - radius; y = 0; } for (unsigned int i = 0; i < circle_points / 2 + 1; i++) polygon.outer().push_back(point_type(cos(angle_step * i + offset) * radius + center.x() - x, sin(angle_step * i + offset) * radius + center.y() - y)); offset += bg::math::pi<double>(); for (unsigned int i = 0; i < circle_points / 2 + 1; i++) polygon.outer().push_back(point_type(cos(angle_step * i + offset) * radius + center.x() + x, sin(angle_step * i + offset) * radius + center.y() + y)); polygon.outer().push_back(polygon.outer().front()); if (hole_diameter != 0) { polygon.inners().resize(1); draw_regular_polygon(center, hole_diameter, circle_points, 0, false, polygon.inners().front()); } }
unique_ptr<multi_polygon_type> GerberImporter::render(bool fill_closed_lines, unsigned int points_per_circle) { map<int, multi_polygon_type> apertures_map; ring_type region; coordinate_type cfactor; unique_ptr<multi_polygon_type> temp_mpoly (new multi_polygon_type()); bool contour = false; vector<pair<const gerbv_layer_t *, gerberimporter_layer> >layers (1); auto layers_equivalent = [](const gerbv_layer_t * const layer1, const gerbv_layer_t * const layer2) { const gerbv_step_and_repeat_t& sr1 = layer1->stepAndRepeat; const gerbv_step_and_repeat_t& sr2 = layer2->stepAndRepeat; if (layer1->polarity == layer2->polarity && sr1.X == sr2.X && sr1.Y == sr2.Y && sr1.dist_X == sr2.dist_X && sr1.dist_Y == sr2.dist_Y) return true; else return false; }; gerbv_image_t *gerber = project->file[0]->image; if (gerber->info->polarity != GERBV_POLARITY_POSITIVE) unsupported_polarity_throw_exception(); if (gerber->netlist->state->unit == GERBV_UNIT_MM) cfactor = scale / 25.4; else cfactor = scale; layers.front().first = gerber->netlist->layer; generate_apertures_map(gerber->aperture, apertures_map, points_per_circle, cfactor); for (gerbv_net_t *currentNet = gerber->netlist; currentNet; currentNet = currentNet->next){ const point_type start (currentNet->start_x * cfactor, currentNet->start_y * cfactor); const point_type stop (currentNet->stop_x * cfactor, currentNet->stop_y * cfactor); const double * const parameters = gerber->aperture[currentNet->aperture]->parameter; multi_polygon_type mpoly; if (!layers_equivalent(currentNet->layer, layers.back().first)) { layers.resize(layers.size() + 1); layers.back().first = currentNet->layer; } map<coordinate_type, multi_linestring_type>& paths = layers.back().second.paths; unique_ptr<multi_polygon_type>& draws = layers.back().second.draws; auto merge_ring = [&](ring_type& ring) { if (ring.size() > 1) { polygon_type polygon; bg::correct(ring); if (simplify_cutins(ring, polygon)) bg::union_(*draws, polygon, *temp_mpoly); else bg::union_(*draws, ring, *temp_mpoly); ring.clear(); draws.swap(temp_mpoly); temp_mpoly->clear(); } }; auto merge_mpoly = [&](multi_polygon_type& mpoly) { bg::correct(mpoly); bg::union_(*draws, mpoly, *temp_mpoly); mpoly.clear(); draws.swap(temp_mpoly); temp_mpoly->clear(); }; if (currentNet->interpolation == GERBV_INTERPOLATION_LINEARx1) { if (currentNet->aperture_state == GERBV_APERTURE_STATE_ON) { if (contour) { if (region.empty()) bg::append(region, start); bg::append(region, stop); } else { if (gerber->aperture[currentNet->aperture]->type == GERBV_APTYPE_CIRCLE) { linestring_type new_segment; new_segment.push_back(start); new_segment.push_back(stop); merge_paths(paths[coordinate_type(gerber->aperture[currentNet->aperture]->parameter[0] * cfactor / 2)], new_segment); } else if (gerber->aperture[currentNet->aperture]->type == GERBV_APTYPE_RECTANGLE) { mpoly.resize(1); linear_draw_rectangular_aperture(start, stop, parameters[0] * cfactor, parameters[1] * cfactor, mpoly.back().outer()); merge_ring(mpoly.back().outer()); } else cerr << "Drawing with an aperture different from a circle " "or a rectangle is forbidden by the Gerber standard; skipping." << endl; } } else if (currentNet->aperture_state == GERBV_APERTURE_STATE_FLASH) { if (contour) { cerr << "D03 during contour mode is forbidden by the Gerber " "standard; skipping" << endl; } else { const auto aperture_mpoly = apertures_map.find(currentNet->aperture); if (aperture_mpoly != apertures_map.end()) bg::transform(aperture_mpoly->second, mpoly, translate(stop.x(), stop.y())); else cerr << "Macro aperture " << currentNet->aperture << " not found in macros list; skipping" << endl; merge_mpoly(mpoly); } } else if (currentNet->aperture_state == GERBV_APERTURE_STATE_OFF) { if (contour) { if (!region.empty()) { bg::append(region, stop); merge_ring(region); } } } else { cerr << "Unrecognized aperture state: skipping" << endl; } } else if (currentNet->interpolation == GERBV_INTERPOLATION_PAREA_START) { contour = true; } else if (currentNet->interpolation == GERBV_INTERPOLATION_PAREA_END) { contour = false; if (!region.empty()) merge_ring(region); } else if (currentNet->interpolation == GERBV_INTERPOLATION_CW_CIRCULAR || currentNet->interpolation == GERBV_INTERPOLATION_CCW_CIRCULAR) { if (currentNet->aperture_state == GERBV_APERTURE_STATE_ON) { const gerbv_cirseg_t * const cirseg = currentNet->cirseg; linestring_type path; if (cirseg != NULL) { double angle1; double angle2; if (currentNet->interpolation == GERBV_INTERPOLATION_CCW_CIRCULAR) { angle1 = cirseg->angle1; angle2 = cirseg->angle2; } else { angle1 = cirseg->angle2; angle2 = cirseg->angle1; } circular_arc(point_type(cirseg->cp_x * scale, cirseg->cp_y * scale), cirseg->width * scale / 2, angle1 * bg::math::pi<double>() / 180.0, angle2 * bg::math::pi<double>() / 180.0, points_per_circle, path); if (contour) { if (region.empty()) copy(path.begin(), path.end(), region.end()); else copy(path.begin() + 1, path.end(), region.end()); } else { if (gerber->aperture[currentNet->aperture]->type == GERBV_APTYPE_CIRCLE) merge_paths(paths[coordinate_type(gerber->aperture[currentNet->aperture]->parameter[0] * cfactor / 2)], path); else cerr << "Drawing an arc with an aperture different from a circle " "is forbidden by the Gerber standard; skipping." << endl; } } else cerr << "Circular arc requested but cirseg == NULL" << endl; } else if (currentNet->aperture_state == GERBV_APERTURE_STATE_FLASH) { cerr << "D03 during circular arc mode is forbidden by the Gerber " "standard; skipping" << endl; } } else if (currentNet->interpolation == GERBV_INTERPOLATION_x10 || currentNet->interpolation == GERBV_INTERPOLATION_LINEARx01 || currentNet->interpolation == GERBV_INTERPOLATION_LINEARx001 ) { cerr << "Linear zoomed interpolation modes are not supported " "(are them in the RS274X standard?)" << endl; } else //if (currentNet->interpolation != GERBV_INTERPOLATION_DELETED) { cerr << "Unrecognized interpolation mode" << endl; } } for (pair<const gerbv_layer_t *, gerberimporter_layer>& layer : layers) { for (pair<const coordinate_type, multi_linestring_type>& path : layer.second.paths) { simplify_paths(path.second); } } return generate_layers(layers, fill_closed_lines, cfactor, points_per_circle); }