void EpsFileWriter::Bezier(SBezier *sb) { Vector c, n = Vector::From(0, 0, 1); double r; if(sb->deg == 1) { MaybeMoveTo(sb->ctrl[0], sb->ctrl[1]); fprintf(f, " %.3f %.3f lineto\r\n", MmToPts(sb->ctrl[1].x - ptMin.x), MmToPts(sb->ctrl[1].y - ptMin.y)); } else if(sb->IsCircle(n, &c, &r)) { Vector p0 = sb->ctrl[0], p1 = sb->ctrl[2]; double theta0 = atan2(p0.y - c.y, p0.x - c.x), theta1 = atan2(p1.y - c.y, p1.x - c.x), dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI); MaybeMoveTo(p0, p1); fprintf(f, " %.3f %.3f %.3f %.3f %.3f %s\r\n", MmToPts(c.x - ptMin.x), MmToPts(c.y - ptMin.y), MmToPts(r), theta0*180/PI, theta1*180/PI, dtheta < 0 ? "arcn" : "arc"); } else if(sb->deg == 3 && !sb->IsRational()) { MaybeMoveTo(sb->ctrl[0], sb->ctrl[3]); fprintf(f, " %.3f %.3f %.3f %.3f %.3f %.3f curveto\r\n", MmToPts(sb->ctrl[1].x - ptMin.x), MmToPts(sb->ctrl[1].y - ptMin.y), MmToPts(sb->ctrl[2].x - ptMin.x), MmToPts(sb->ctrl[2].y - ptMin.y), MmToPts(sb->ctrl[3].x - ptMin.x), MmToPts(sb->ctrl[3].y - ptMin.y)); } else { BezierAsNonrationalCubic(sb); } }
void DxfFileWriter::Bezier(SBezier *sb) { Vector c, n = Vector::From(0, 0, 1); double r; if(sb->deg == 1) { fprintf(f, " 0\r\n" "LINE\r\n" " 8\r\n" // Layer code "%d\r\n" " 10\r\n" // xA "%.6f\r\n" " 20\r\n" // yA "%.6f\r\n" " 30\r\n" // zA "%.6f\r\n" " 11\r\n" // xB "%.6f\r\n" " 21\r\n" // yB "%.6f\r\n" " 31\r\n" // zB "%.6f\r\n", 0, sb->ctrl[0].x, sb->ctrl[0].y, sb->ctrl[0].z, sb->ctrl[1].x, sb->ctrl[1].y, sb->ctrl[1].z); } else if(sb->IsInPlane(n, 0) && sb->IsCircle(n, &c, &r)) { double theta0 = atan2(sb->ctrl[0].y - c.y, sb->ctrl[0].x - c.x), theta1 = atan2(sb->ctrl[2].y - c.y, sb->ctrl[2].x - c.x), dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI); if(dtheta < 0) { SWAP(double, theta0, theta1); } fprintf(f, " 0\r\n" "ARC\r\n" " 8\r\n" // Layer code "%d\r\n" " 10\r\n" // x "%.6f\r\n" " 20\r\n" // y "%.6f\r\n" " 30\r\n" // z "%.6f\r\n" " 40\r\n" // radius "%.6f\r\n" " 50\r\n" // start angle "%.6f\r\n" " 51\r\n" // end angle "%.6f\r\n", 0, c.x, c.y, 0.0, r, theta0*180/PI, theta1*180/PI); } else { BezierAsPwl(sb); } }
SSurface SSurface::FromRevolutionOf(SBezier *sb, Vector pt, Vector axis, double thetas, double thetaf) { SSurface ret = {}; ret.degm = sb->deg; ret.degn = 2; double dtheta = fabs(WRAP_SYMMETRIC(thetaf - thetas, 2*PI)); // We now wish to revolve the curve about the z axis int i; for(i = 0; i <= ret.degm; i++) { Vector p = sb->ctrl[i]; Vector ps = p.RotatedAbout(pt, axis, thetas), pf = p.RotatedAbout(pt, axis, thetaf); Vector ct; if(ps.Equals(pf)) { // Degenerate case: a control point lies on the axis of revolution, // so we get three coincident control points. ct = ps; } else { // Normal case, the control point sweeps out a circle. Vector c = ps.ClosestPointOnLine(pt, axis); Vector rs = ps.Minus(c), rf = pf.Minus(c); Vector ts = axis.Cross(rs), tf = axis.Cross(rf); ct = Vector::AtIntersectionOfLines(ps, ps.Plus(ts), pf, pf.Plus(tf), NULL, NULL, NULL); } ret.ctrl[i][0] = ps; ret.ctrl[i][1] = ct; ret.ctrl[i][2] = pf; ret.weight[i][0] = sb->weight[i]; ret.weight[i][1] = sb->weight[i]*cos(dtheta/2); ret.weight[i][2] = sb->weight[i]; } return ret; }
void SvgFileWriter::Bezier(SBezier *sb) { Vector c, n = Vector::From(0, 0, 1); double r; if(sb->deg == 1) { MaybeMoveTo(sb->ctrl[0], sb->ctrl[1]); fprintf(f, "L%.3f,%.3f ", (sb->ctrl[1].x - ptMin.x), (ptMax.y - sb->ctrl[1].y)); } else if(sb->IsCircle(n, &c, &r)) { Vector p0 = sb->ctrl[0], p1 = sb->ctrl[2]; double theta0 = atan2(p0.y - c.y, p0.x - c.x), theta1 = atan2(p1.y - c.y, p1.x - c.x), dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI); // The arc must be less than 180 degrees, or else it couldn't have // been represented as a single rational Bezier. So large-arc-flag // must be false. sweep-flag is determined by the sign of dtheta. // Note that clockwise and counter-clockwise are backwards in SVG's // mirrored csys. MaybeMoveTo(p0, p1); fprintf(f, "A%.3f,%.3f 0 0,%d %.3f,%.3f ", r, r, (dtheta < 0) ? 1 : 0, p1.x - ptMin.x, ptMax.y - p1.y); } else if(!sb->IsRational()) { if(sb->deg == 2) { MaybeMoveTo(sb->ctrl[0], sb->ctrl[2]); fprintf(f, "Q%.3f,%.3f %.3f,%.3f ", sb->ctrl[1].x - ptMin.x, ptMax.y - sb->ctrl[1].y, sb->ctrl[2].x - ptMin.x, ptMax.y - sb->ctrl[2].y); } else if(sb->deg == 3) { MaybeMoveTo(sb->ctrl[0], sb->ctrl[3]); fprintf(f, "C%.3f,%.3f %.3f,%.3f %.3f,%.3f ", sb->ctrl[1].x - ptMin.x, ptMax.y - sb->ctrl[1].y, sb->ctrl[2].x - ptMin.x, ptMax.y - sb->ctrl[2].y, sb->ctrl[3].x - ptMin.x, ptMax.y - sb->ctrl[3].y); } } else { BezierAsNonrationalCubic(sb); } }
bool TextWindow::EditControlDoneForPaste(char *s) { Expr *e; switch(edit.meaning) { case EDIT_PASTE_TIMES_REPEATED: { e = Expr::From(s, true); if(!e) break; int v = (int)e->Eval(); if(v > 0) { shown.paste.times = v; } else { Error("Number of copies to paste must be at least one."); } break; } case EDIT_PASTE_ANGLE: e = Expr::From(s, true); if(!e) break; shown.paste.theta = WRAP_SYMMETRIC((e->Eval())*PI/180, 2*PI); break; case EDIT_PASTE_SCALE: { e = Expr::From(s, true); double v = e->Eval(); if(fabs(v) > 1e-6) { shown.paste.scale = v; } else { Error("Scale cannot be zero."); } break; } default: return false; } return true; }
//----------------------------------------------------------------------------- // Find all points where a line through a and b intersects our surface, and // add them to the list. If seg is true then report only intersections that // lie within the finite line segment (not including the endpoints); otherwise // we work along the infinite line. And we report either just intersections // inside the trim curve, or any intersection with u, v in [0, 1]. And we // either disregard or report tangent points. //----------------------------------------------------------------------------- void SSurface::AllPointsIntersecting(Vector a, Vector b, List<SInter> *l, bool seg, bool trimmed, bool inclTangent) { if(LineEntirelyOutsideBbox(a, b, seg)) return; Vector ba = b.Minus(a); double bam = ba.Magnitude(); List<Inter> inters; ZERO(&inters); // All the intersections between the line and the surface; either special // cases that we can quickly solve in closed form, or general numerical. Vector center, axis, start, finish; double radius; if(degm == 1 && degn == 1) { // Against a plane, easy. Vector n = NormalAt(0, 0).WithMagnitude(1); double d = n.Dot(PointAt(0, 0)); // Trim to line segment now if requested, don't generate points that // would just get discarded later. if(!seg || (n.Dot(a) > d + LENGTH_EPS && n.Dot(b) < d - LENGTH_EPS) || (n.Dot(b) > d + LENGTH_EPS && n.Dot(a) < d - LENGTH_EPS)) { Vector p = Vector::AtIntersectionOfPlaneAndLine(n, d, a, b, NULL); Inter inter; ClosestPointTo(p, &(inter.p.x), &(inter.p.y)); inters.Add(&inter); } } else if(IsCylinder(&axis, ¢er, &radius, &start, &finish)) { // This one can be solved in closed form too. Vector ab = b.Minus(a); if(axis.Cross(ab).Magnitude() < LENGTH_EPS) { // edge is parallel to axis of cylinder, no intersection points return; } // A coordinate system centered at the center of the circle, with // the edge under test horizontal Vector u, v, n = axis.WithMagnitude(1); u = (ab.Minus(n.ScaledBy(ab.Dot(n)))).WithMagnitude(1); v = n.Cross(u); Point2d ap = (a.Minus(center)).DotInToCsys(u, v, n).ProjectXy(), bp = (b.Minus(center)).DotInToCsys(u, v, n).ProjectXy(), sp = (start. Minus(center)).DotInToCsys(u, v, n).ProjectXy(), fp = (finish.Minus(center)).DotInToCsys(u, v, n).ProjectXy(); double thetas = atan2(sp.y, sp.x), thetaf = atan2(fp.y, fp.x); Point2d ip[2]; int ip_n = 0; if(fabs(fabs(ap.y) - radius) < LENGTH_EPS) { // tangent if(inclTangent) { ip[0] = Point2d::From(0, ap.y); ip_n = 1; } } else if(fabs(ap.y) < radius) { // two intersections double xint = sqrt(radius*radius - ap.y*ap.y); ip[0] = Point2d::From(-xint, ap.y); ip[1] = Point2d::From( xint, ap.y); ip_n = 2; } int i; for(i = 0; i < ip_n; i++) { double t = (ip[i].Minus(ap)).DivPivoting(bp.Minus(ap)); // This is a point on the circle; but is it on the arc? Point2d pp = ap.Plus((bp.Minus(ap)).ScaledBy(t)); double theta = atan2(pp.y, pp.x); double dp = WRAP_SYMMETRIC(theta - thetas, 2*PI), df = WRAP_SYMMETRIC(thetaf - thetas, 2*PI); double tol = LENGTH_EPS/radius; if((df > 0 && ((dp < -tol) || (dp > df + tol))) || (df < 0 && ((dp > tol) || (dp < df - tol)))) { continue; } Vector p = a.Plus((b.Minus(a)).ScaledBy(t)); Inter inter; ClosestPointTo(p, &(inter.p.x), &(inter.p.y)); inters.Add(&inter); } } else { // General numerical solution by subdivision, fallback int cnt = 0, level = 0; AllPointsIntersectingUntrimmed(a, b, &cnt, &level, &inters, seg, this); } // Remove duplicate intersection points inters.ClearTags(); int i, j; for(i = 0; i < inters.n; i++) { for(j = i + 1; j < inters.n; j++) { if(inters.elem[i].p.Equals(inters.elem[j].p)) { inters.elem[j].tag = 1; } } } inters.RemoveTagged(); for(i = 0; i < inters.n; i++) { Point2d puv = inters.elem[i].p; // Make sure the point lies within the finite line segment Vector pxyz = PointAt(puv.x, puv.y); double t = (pxyz.Minus(a)).DivPivoting(ba); if(seg && (t > 1 - LENGTH_EPS/bam || t < LENGTH_EPS/bam)) { continue; } // And that it lies inside our trim region Point2d dummy = { 0, 0 }; int c = bsp->ClassifyPoint(puv, dummy, this); if(trimmed && c == SBspUv::OUTSIDE) { continue; } // It does, so generate the intersection SInter si; si.p = pxyz; si.surfNormal = NormalAt(puv.x, puv.y); si.pinter = puv; si.srf = this; si.onEdge = (c != SBspUv::INSIDE); l->Add(&si); } inters.Clear(); }
bool TextWindow::EditControlDoneForStyles(const char *str) { Style *s; switch(edit.meaning) { case EDIT_STYLE_TEXT_HEIGHT: case EDIT_STYLE_WIDTH: { SS.UndoRemember(); s = Style::Get(edit.style); double v; int units = (edit.meaning == EDIT_STYLE_TEXT_HEIGHT) ? s->textHeightAs : s->widthAs; if(units == Style::UNITS_AS_MM) { v = SS.StringToMm(str); } else { v = atof(str); } v = max(0, v); if(edit.meaning == EDIT_STYLE_TEXT_HEIGHT) { s->textHeight = v; } else { s->width = v; } break; } case EDIT_STYLE_TEXT_ANGLE: SS.UndoRemember(); s = Style::Get(edit.style); s->textAngle = WRAP_SYMMETRIC(atof(str), 360); break; case EDIT_BACKGROUND_COLOR: case EDIT_STYLE_FILL_COLOR: case EDIT_STYLE_COLOR: { Vector rgb; if(sscanf(str, "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) { rgb = rgb.ClampWithin(0, 1); if(edit.meaning == EDIT_STYLE_COLOR) { SS.UndoRemember(); s = Style::Get(edit.style); s->color = RGBf(rgb.x, rgb.y, rgb.z); } else if(edit.meaning == EDIT_STYLE_FILL_COLOR) { SS.UndoRemember(); s = Style::Get(edit.style); s->fillColor = RGBf(rgb.x, rgb.y, rgb.z); } else { SS.backgroundColor = RGBf(rgb.x, rgb.y, rgb.z); } } else { Error("Bad format: specify color as r, g, b"); } break; } case EDIT_STYLE_NAME: if(!StringAllPrintable(str) || !*str) { Error("Invalid characters. Allowed are: A-Z a-z 0-9 _ -"); } else { SS.UndoRemember(); s = Style::Get(edit.style); s->name.strcpy(str); } break; case EDIT_BACKGROUND_IMG_SCALE: { Expr *e = Expr::From(str, true); if(e) { double ev = e->Eval(); if(ev < 0.001 || isnan(ev)) { Error("Scale must not be zero or negative!"); } else { SS.bgImage.scale = ev / SS.MmPerUnit(); } } break; } default: return false; } return true; }