1
0
mirror of git://projects.qi-hardware.com/cae-tools.git synced 2024-12-23 20:35:30 +02:00
cae-tools/cameo/path.c

473 lines
8.7 KiB
C

/*
* path.c - Toolpath operations
*
* Written 2010-2012 by Werner Almesberger
* Copyright 2010-2012 Werner Almesberger
*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include "util.h"
#include "shape.h"
#include "poly2d.h"
#include "path.h"
/*
* We allow for a bit of tolerance, to absorb the rounding errors KiCad
* produces with designs using a metric grid.
*/
#define EPSILON_MM 0.006 /* 6 um */
static void free_points(struct point *points)
{
struct point *next;
while (points) {
next = points->next;
free(points);
points = next;
}
}
void path_free(struct path *path)
{
free_points(path->first);
free((void *) path->id);
free(path);
}
struct path *path_new(double r_tool, const char *id)
{
struct path *path;
path = alloc_type(struct path);
path->r_tool = r_tool;
path->outside = 0;
path->notch = 0;
path->id = id ? stralloc(id) : NULL;
path->first = path->last = NULL;
path->next = NULL;
return path;
}
static struct path *path_from(const struct path *old)
{
struct path *new;
new = path_new(old->r_tool, old->id);
new->outside = old->outside;
new->notch = old->notch;
return new;
}
static struct point *clone_point(const struct point *p)
{
struct point *n;
n = alloc_type(struct point);
n->x = p->x;
n->y = p->y;
n->z = p->z;
n->next = NULL;
return n;
}
static int points_eq(const struct point *a, const struct point *b)
{
if (hypot(a->x-b->x, a->y-b->y) > EPSILON_MM)
return 0;
if (fabs(a->z-b->z) > EPSILON_MM)
return 0;
return 1;
}
int path_is_closed(const struct path *path)
{
if (path->first == path->last)
return 1;
return points_eq(path->first, path->last);
}
static void assert_path_is_closed(const struct path *path)
{
if (path_is_closed(path))
return;
fprintf(stderr, "path from (%g, %g, %g) to (%g, %g, %g) is open\n",
path->first->x, path->first->y, path->first->z,
path->last->x, path->last->y, path->last->z);
abort();
}
static void path_add_point(struct path *path, struct point *p)
{
if (path->last &&
path->last->x == p->x && path->last->y == p->y &&
path->last->z == p->z) {
free(p);
return;
}
p->next = NULL;
if (path->last)
path->last->next = p;
else
path->first = p;
path->last = p;
}
void path_add(struct path *path, double x, double y, double z)
{
struct point *p;
p = alloc_type(struct point);
p->x = x;
p->y = y;
p->z = z;
return path_add_point(path, p);
}
struct path *path_reverse(const struct path *path)
{
struct path *new;
const struct point *p;
struct point *n;
new = path_from(path);
for (p = path->first; p; p = p->next) {
n = alloc_type(struct point);
n->x = p->x;
n->y = p->y;
n->z = p->z;
n->next = new->first;
if (!new->last)
new->last = n;
new->first = n;
}
return new;
}
struct path *path_clone(const struct path *path)
{
struct path *new;
const struct point *p;
struct point *n;
new = path_from(path);
for (p = path->first; p; p = p->next) {
n = clone_point(p);
if (new->first)
new->last->next = n;
else
new->first = n;
new->last = n;
}
return new;
}
static void path_reverse_inplace(struct path *path)
{
struct point *points = NULL, *p, *next;
path->last = path->first;
for (p = path->first; p; p = next) {
next = p->next;
p->next = points;
points = p;
}
path->first = points;
}
static struct point *offset_point(const struct point *a, const struct point *b,
const struct point *c, double off, int left)
{
double ax, ay, bx, by;
double aa, bb;
double nx, ny;
double angle, f;
struct point *p;
ax = b->x-a->x;
ay = b->y-a->y;
bx = c->x-b->x;
by = c->y-b->y;
aa = hypot(ax, ay);
bb = hypot(bx, by);
if (left) {
nx = -(ay/aa+by/bb);
ny = ax/aa+bx/bb;
} else {
nx = ay/aa+by/bb;
ny = -(ax/aa+bx/bb);
}
/* angle between AB and BC */
angle = acos(-(ax*bx+ay*by)/aa/bb);
/* multiplier for combination of normal vectors */
f = off/sin(angle/2);
f /= hypot(nx, ny);
nx *= f;
ny *= f;
p = alloc_type(struct point);
p->x = b->x+nx;
p->y = b->y+ny;
p->z = b->z;
p->next = NULL;
return p;
}
static int left_turn(const struct point *a, const struct point *b,
const struct point *c)
{
double ax, ay, bx, by;
ax = b->x-a->x;
ay = b->y-a->y;
bx = c->x-b->x;
by = c->y-b->y;
return (ax*by-ay*bx) >= 0;
}
/*
* Angle in counter-clockwise direction to turn at point B when coming from A
* in order to face towards C.
*/
static double angle_3(const struct point *a, const struct point *b,
const struct point *c)
{
double ax, ay, bx, by;
double aa, bb;
double angle;
ax = b->x-a->x;
ay = b->y-a->y;
bx = c->x-b->x;
by = c->y-b->y;
aa = hypot(ax, ay);
bb = hypot(bx, by);
angle = acos((ax*bx+ay*by)/aa/bb)/M_PI*180.0;
return (ax*by-ay*bx) >= 0 ? angle : -angle;
}
/*
* If we predominantly turn to the right, then the tool must be on the
* left-hand side. Otherwise, it's on the right.
*/
int path_tool_is_left(const struct path *path)
{
const struct point *prev, *p, *next;
double a = 0;
assert_path_is_closed(path);
prev = path->first;
for (p = path->first->next; p; p = p->next) {
next = p->next ? p->next : path->first->next;
a += angle_3(prev, p, next);
prev = p;
}
return a < 0;
}
/*
* http://www.makecnc.com/bones.jpg
*/
static struct point *dog_point(const struct point *edge,
const struct point *tool, double off)
{
double vx, vy, v;
struct point *p;
vx = edge->x-tool->x;
vy = edge->y-tool->y;
v = hypot(vx, vy);
vx *= 1-off/v;
vy *= 1-off/v;
p = alloc_type(struct point);
p->x = tool->x+vx;
p->y = tool->y+vy;
p->z = tool->z;
p->next = NULL;
return p;
}
/*
* The tool is on the "left" side of the path. E.g., if the path goes from
* 6 o'clock to 12 o'clock, the tool would be at 9 o'clock.
*/
struct path *path_offset(const struct path *path, int left, int notch)
{
struct path *new;
const struct point *prev, *p, *next;
struct point *n, *n2;
int dog;
assert_path_is_closed(path);
if (path->first == path->last)
return circle(path->first->x, path->first->y, path->first->z,
path->r_tool, path->r_tool, 0.1, path->id);
new = path_from(path);
prev = path->first;
for (p = path->first->next; p; p = p->next) {
next = p->next ? p->next : path->first->next;
n = offset_point(prev, p, next, path->r_tool, left);
dog = notch && left_turn(prev, p, next) == left;
if (dog)
n2 = clone_point(n);
path_add_point(new, n);
if (dog) {
path_add_point(new, dog_point(p, n2, path->r_tool));
path_add_point(new, n2);
}
prev = p;
}
path_add_point(new, clone_point(new->first));
return new;
}
const struct path *path_find_leftmost(const struct path *path)
{
const struct point *p;
const struct path *best = NULL;
double best_x = HUGE_VAL;
while (path) {
for (p = path->first; p; p = p->next)
if (p->x < best_x) {
best = path;
best_x = p->x;
break;
}
path = path->next;
}
return best;
}
int path_is_inside(const struct path *a, const struct path *b)
{
struct p2d *pa, *pb;
int res;
pa = path_to_poly(a);
pb = path_to_poly(b);
res = p2d_contains_poly(pb, pa);
p2d_free(pa);
p2d_free(pb);
return res == 1; /* 0 if they intersect */
}
static int attr_eq(const struct path *a, const struct path *b)
{
return a->r_tool == b->r_tool && a->outside == b->outside &&
a->notch == b->notch;
}
struct path *path_connect(struct path *path)
{
struct path **a, **b;
struct path *tmp;
again:
for (a = &path; *a; a = &(*a)->next)
for (b = &(*a)->next; *b; b = &(*b)->next) {
if (!attr_eq(*a, *b))
continue;
if (points_eq((*a)->last, (*b)->last))
path_reverse_inplace(*b);
if (points_eq((*a)->last, (*b)->first)) {
(*a)->last->next = (*b)->first->next;
(*a)->last = (*b)->last;
free((*b)->first);
tmp = *b;
*b = tmp->next;
free(tmp);
goto again;
}
if (points_eq((*a)->first, (*b)->first))
path_reverse_inplace(*b);
if (points_eq((*a)->first, (*b)->last)) {
(*b)->last->next = (*a)->first->next;
(*b)->last = (*a)->last;
free((*a)->first);
tmp = *a;
*a = tmp->next;
free(tmp);
goto again;
}
}
return path;
}
void path_stats(const struct path *path)
{
int paths = 0, segs = 0;
double len = 0;
const struct point *p;
while (path) {
paths++;
for (p = path->first; p; p = p->next) {
if (!p->next)
continue;
segs++;
len += hypot(hypot(p->x-p->next->x, p->y-p->next->y),
p->z-p->next->z);
}
path = path->next;
}
fprintf(stderr, "%d path%s, %d segment%s, %f mm\n",
paths, paths == 1 ? "" : "s", segs, segs == 1 ? "" : "s", len);
}