Entities now generate rational polynomial curves instead of

piecwise linear segments. These are piecewise linear approximated
for display, and currently for the mesh too, but that's the first
step to replace the mesh with exact curved surfaces.

[git-p4: depot-paths = "//depot/solvespace/": change = 1895]
solver
Jonathan Westhues 2009-01-14 19:55:42 -08:00
parent b8da4ed2b3
commit f904c0fbee
7 changed files with 309 additions and 186 deletions

View File

@ -3,7 +3,7 @@ DEFINES = /D_WIN32_WINNT=0x500 /DISOLATION_AWARE_ENABLED /D_WIN32_IE=0x500 /DWIN
# happens if those mix, but don't want to risk it. # happens if those mix, but don't want to risk it.
CFLAGS = /W3 /nologo -MT -Iextlib -I..\common\win32 /D_DEBUG /D_CRT_SECURE_NO_WARNINGS /I. /Zi /EHs CFLAGS = /W3 /nologo -MT -Iextlib -I..\common\win32 /D_DEBUG /D_CRT_SECURE_NO_WARNINGS /I. /Zi /EHs
HEADERS = ..\common\win32\freeze.h ui.h solvespace.h dsc.h sketch.h expr.h polygon.h HEADERS = ..\common\win32\freeze.h ui.h solvespace.h dsc.h sketch.h expr.h polygon.h srf\surface.h
OBJDIR = obj OBJDIR = obj
@ -39,6 +39,9 @@ SSOBJS = $(OBJDIR)\solvespace.obj \
$(OBJDIR)\generate.obj \ $(OBJDIR)\generate.obj \
$(OBJDIR)\export.obj \ $(OBJDIR)\export.obj \
SRFOBJS = $(OBJDIR)\ratpoly.obj \
RES = $(OBJDIR)\resource.res RES = $(OBJDIR)\resource.res
@ -52,14 +55,17 @@ all: $(OBJDIR)/solvespace.exe
clean: clean:
rm -f obj/* rm -f obj/*
$(OBJDIR)/solvespace.exe: $(SSOBJS) $(W32OBJS) $(FREEZE) $(RES) $(OBJDIR)/solvespace.exe: $(SSOBJS) $(SRFOBJS) $(W32OBJS) $(FREEZE) $(RES)
@$(CC) $(DEFINES) $(CFLAGS) -Fe$(OBJDIR)/solvespace.exe $(SSOBJS) $(W32OBJS) $(FREEZE) $(RES) $(LIBS) @$(CC) $(DEFINES) $(CFLAGS) -Fe$(OBJDIR)/solvespace.exe $(SSOBJS) $(SRFOBJS) $(W32OBJS) $(FREEZE) $(RES) $(LIBS)
editbin /nologo /STACK:8388608 $(OBJDIR)/solvespace.exe editbin /nologo /STACK:8388608 $(OBJDIR)/solvespace.exe
@echo solvespace.exe @echo solvespace.exe
$(SSOBJS): $(@B).cpp $(HEADERS) $(SSOBJS): $(@B).cpp $(HEADERS)
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj $(@B).cpp @$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj $(@B).cpp
$(SRFOBJS): srf\$(@B).cpp $(HEADERS)
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj srf\$(@B).cpp
$(W32OBJS): win32/$(@B).cpp $(HEADERS) $(W32OBJS): win32/$(@B).cpp $(HEADERS)
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj win32/$(@B).cpp @$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj win32/$(@B).cpp

View File

@ -21,13 +21,6 @@ void Entity::LineDrawOrGetDistance(Vector a, Vector b) {
dogd.refp = (a.Plus(b)).ScaledBy(0.5); dogd.refp = (a.Plus(b)).ScaledBy(0.5);
} }
void Entity::LineDrawOrGetDistanceOrEdge(Vector a, Vector b) {
LineDrawOrGetDistance(a, b);
if(dogd.edges && !construction) {
dogd.edges->AddEdge(a, b);
}
}
void Entity::DrawAll(void) { void Entity::DrawAll(void) {
// This handles points and line segments as a special case, because I // This handles points and line segments as a special case, because I
// seem to be able to get a huge speedup that way, by consolidating // seem to be able to get a huge speedup that way, by consolidating
@ -96,20 +89,34 @@ void Entity::DrawAll(void) {
void Entity::Draw(void) { void Entity::Draw(void) {
dogd.drawing = true; dogd.drawing = true;
dogd.edges = NULL;
DrawOrGetDistance(); DrawOrGetDistance();
} }
void Entity::GenerateEdges(SEdgeList *el) { void Entity::GenerateEdges(SEdgeList *el, bool includingConstruction) {
dogd.drawing = false; if(construction && !includingConstruction) return;
dogd.edges = el;
DrawOrGetDistance(); SPolyCurveList spcl;
dogd.edges = NULL; ZERO(&spcl);
GeneratePolyCurves(&spcl);
int i, j;
for(i = 0; i < spcl.l.n; i++) {
SPolyCurve *spc = &(spcl.l.elem[i]);
List<Vector> lv;
ZERO(&lv);
spc->MakePwlInto(&lv);
for(j = 1; j < lv.n; j++) {
el->AddEdge(lv.elem[j-1], lv.elem[j]);
}
lv.Clear();
}
spcl.Clear();
} }
double Entity::GetDistance(Point2d mp) { double Entity::GetDistance(Point2d mp) {
dogd.drawing = false; dogd.drawing = false;
dogd.edges = NULL;
dogd.mp = mp; dogd.mp = mp;
dogd.dmin = 1e12; dogd.dmin = 1e12;
@ -120,7 +127,6 @@ double Entity::GetDistance(Point2d mp) {
Vector Entity::GetReferencePos(void) { Vector Entity::GetReferencePos(void) {
dogd.drawing = false; dogd.drawing = false;
dogd.edges = NULL;
dogd.refp = SS.GW.offset.ScaledBy(-1); dogd.refp = SS.GW.offset.ScaledBy(-1);
DrawOrGetDistance(); DrawOrGetDistance();
@ -157,48 +163,102 @@ bool Entity::IsVisible(void) {
return true; return true;
} }
Vector Entity::BezierEval(double t, Vector p0, Vector p1, Vector p2, Vector p3) void Entity::GeneratePolyCurves(SPolyCurveList *spcl) {
{ SPolyCurve spc;
return (p0.ScaledBy((1 - t)*(1 - t)*(1 - t))).Plus(
(p1.ScaledBy(3*t*(1 - t)*(1 - t))).Plus( switch(type) {
(p2.ScaledBy(3*t*t*(1 - t))).Plus( case LINE_SEGMENT: {
(p3.ScaledBy(t*t*t))))); Vector a = SS.GetEntity(point[0])->PointGetNum();
Vector b = SS.GetEntity(point[1])->PointGetNum();
spc = SPolyCurve::From(a, b);
spcl->l.Add(&spc);
break;
}
case CUBIC: {
Vector p0 = SS.GetEntity(point[0])->PointGetNum();
Vector p1 = SS.GetEntity(point[1])->PointGetNum();
Vector p2 = SS.GetEntity(point[2])->PointGetNum();
Vector p3 = SS.GetEntity(point[3])->PointGetNum();
spc = SPolyCurve::From(p0, p1, p2, p3);
spcl->l.Add(&spc);
break;
} }
void Entity::BezierPwl(double ta, double tb, case CIRCLE:
Vector p0, Vector p1, Vector p2, Vector p3) case ARC_OF_CIRCLE: {
{ Vector center = SS.GetEntity(point[0])->PointGetNum();
Vector pa = BezierEval(ta, p0, p1, p2, p3); Quaternion q = SS.GetEntity(normal)->NormalGetNum();
Vector pb = BezierEval(tb, p0, p1, p2, p3); Vector u = q.RotationU(), v = q.RotationV();
double r = CircleGetRadiusNum();
double thetaa, thetab, dtheta;
double tm1 = (2*ta + tb) / 3; if(type == CIRCLE) {
double tm2 = (ta + 2*tb) / 3; thetaa = 0;
thetab = 2*PI;
Vector pm1 = BezierEval(tm1, p0, p1, p2, p3); dtheta = 2*PI;
Vector pm2 = BezierEval(tm2, p0, p1, p2, p3);
double d = max(pm1.DistanceToLine(pa, pb.Minus(pa)),
pm2.DistanceToLine(pa, pb.Minus(pa)));
double tol = SS.chordTol/SS.GW.scale;
double step = 1.0/SS.maxSegments;
if((tb - ta) < step || d < tol) {
LineDrawOrGetDistanceOrEdge(pa, pb);
} else { } else {
double tm = (ta + tb) / 2; ArcGetAngles(&thetaa, &thetab, &dtheta);
BezierPwl(ta, tm, p0, p1, p2, p3);
BezierPwl(tm, tb, p0, p1, p2, p3);
} }
int i, n;
if(dtheta > 3*PI/2) {
n = 4;
} else if(dtheta > PI) {
n = 3;
} else if(dtheta > PI/2) {
n = 2;
} else {
n = 1;
}
dtheta /= n;
for(i = 0; i < n; i++) {
double s, c;
c = cos(thetaa);
s = sin(thetaa);
// The start point of the curve, and the tangent vector at
// that start point.
Vector p0 = center.Plus(u.ScaledBy( r*c)).Plus(v.ScaledBy(r*s)),
t0 = u.ScaledBy(-r*s). Plus(v.ScaledBy(r*c));
thetaa += dtheta;
c = cos(thetaa);
s = sin(thetaa);
Vector p2 = center.Plus(u.ScaledBy( r*c)).Plus(v.ScaledBy(r*s)),
t2 = u.ScaledBy(-r*s). Plus(v.ScaledBy(r*c));
// The control point must lie on both tangents.
Vector p1 = Vector::AtIntersectionOfLines(p0, p0.Plus(t0),
p2, p2.Plus(t2),
NULL);
SPolyCurve spc = SPolyCurve::From(p0, p1, p2);
spc.weight[1] = cos(dtheta/2);
spcl->l.Add(&spc);
}
break;
} }
case TTF_TEXT: {
Vector topLeft = SS.GetEntity(point[0])->PointGetNum();
Vector botLeft = SS.GetEntity(point[1])->PointGetNum();
Vector n = Normal()->NormalN();
Vector v = topLeft.Minus(botLeft);
Vector u = (v.Cross(n)).WithMagnitude(v.Magnitude());
SS.fonts.PlotString(font.str, str.str, 0, spcl, botLeft, u, v);
break;
}
default:
// Not a problem, points and normals and such don't generate curves
break;
}
}
void Entity::DrawOrGetDistance(void) { void Entity::DrawOrGetDistance(void) {
// If an entity is invisible, then it doesn't get shown, and it doesn't
// contribute a distance for the selection, but it still generates edges.
if(!dogd.edges) {
if(!IsVisible()) return; if(!IsVisible()) return;
}
Group *g = SS.GetGroup(group); Group *g = SS.GetGroup(group);
@ -350,77 +410,13 @@ void Entity::DrawOrGetDistance(void) {
break; break;
} }
case LINE_SEGMENT: { case LINE_SEGMENT:
Vector a = SS.GetEntity(point[0])->PointGetNum(); case CIRCLE:
Vector b = SS.GetEntity(point[1])->PointGetNum(); case ARC_OF_CIRCLE:
LineDrawOrGetDistanceOrEdge(a, b); case CUBIC:
case TTF_TEXT:
// Nothing but the curve(s).
break; break;
}
case CUBIC: {
Vector p0 = SS.GetEntity(point[0])->PointGetNum();
Vector p1 = SS.GetEntity(point[1])->PointGetNum();
Vector p2 = SS.GetEntity(point[2])->PointGetNum();
Vector p3 = SS.GetEntity(point[3])->PointGetNum();
BezierPwl(0, 1, p0, p1, p2, p3);
break;
}
case ARC_OF_CIRCLE: {
Vector c = SS.GetEntity(point[0])->PointGetNum();
Vector pa = SS.GetEntity(point[1])->PointGetNum();
Vector pb = SS.GetEntity(point[2])->PointGetNum();
Quaternion q = SS.GetEntity(normal)->NormalGetNum();
Vector u = q.RotationU(), v = q.RotationV();
double ra = (pa.Minus(c)).Magnitude();
double rb = (pb.Minus(c)).Magnitude();
double thetaa, thetab, dtheta;
ArcGetAngles(&thetaa, &thetab, &dtheta);
int i, n = 3 + (int)(SS.CircleSides(ra)*dtheta/(2*PI));
Vector prev = pa;
for(i = 1; i <= n; i++) {
double theta = thetaa + (dtheta*i)/n;
double r = ra + ((rb - ra)*i)/n;
Vector d = u.ScaledBy(cos(theta)).Plus(v.ScaledBy(sin(theta)));
Vector p = c.Plus(d.ScaledBy(r));
LineDrawOrGetDistanceOrEdge(prev, p);
prev = p;
}
break;
}
case CIRCLE: {
Quaternion q = SS.GetEntity(normal)->NormalGetNum();
double r = SS.GetEntity(distance)->DistanceGetNum();
Vector center = SS.GetEntity(point[0])->PointGetNum();
Vector u = q.RotationU(), v = q.RotationV();
int i, c = SS.CircleSides(r);
Vector prev = u.ScaledBy(r).Plus(center);
for(i = 1; i <= c; i++) {
double phi = (2*PI*i)/c;
Vector p = (u.ScaledBy(r*cos(phi))).Plus(
v.ScaledBy(r*sin(phi)));
p = p.Plus(center);
LineDrawOrGetDistanceOrEdge(prev, p);
prev = p;
}
break;
}
case TTF_TEXT: {
Vector topLeft = SS.GetEntity(point[0])->PointGetNum();
Vector botLeft = SS.GetEntity(point[1])->PointGetNum();
Vector n = Normal()->NormalN();
Vector v = topLeft.Minus(botLeft);
Vector u = (v.Cross(n)).WithMagnitude(v.Magnitude());
SS.fonts.PlotString(font.str, str.str, 0, h, botLeft, u, v);
break;
}
case FACE_NORMAL_PT: case FACE_NORMAL_PT:
case FACE_XPROD: case FACE_XPROD:
@ -433,5 +429,17 @@ void Entity::DrawOrGetDistance(void) {
default: default:
oops(); oops();
} }
// And draw the curves; generate the rational polynomial curves for
// everything, then piecewise linearize them, and display those.
SEdgeList sel;
ZERO(&sel);
GenerateEdges(&sel, true);
int i;
for(i = 0; i < sel.l.n; i++) {
SEdge *se = &(sel.l.elem[i]);
LineDrawOrGetDistance(se->a, se->b);
}
sel.Clear();
} }

View File

@ -393,21 +393,17 @@ public:
bool drawing; bool drawing;
Point2d mp; Point2d mp;
double dmin; double dmin;
SEdgeList *edges;
Vector refp; Vector refp;
} dogd; // state for drawing or getting distance (for hit testing) } dogd; // state for drawing or getting distance (for hit testing)
void LineDrawOrGetDistance(Vector a, Vector b); void LineDrawOrGetDistance(Vector a, Vector b);
void LineDrawOrGetDistanceOrEdge(Vector a, Vector b);
void DrawOrGetDistance(void); void DrawOrGetDistance(void);
void BezierPwl(double ta, double tb, void GeneratePolyCurves(SPolyCurveList *spcl);
Vector p0, Vector p1, Vector p2, Vector p3); void GenerateEdges(SEdgeList *el, bool includingConstruction=false);
Vector BezierEval(double t, Vector p0, Vector p1, Vector p2, Vector p3);
static void DrawAll(void); static void DrawAll(void);
void Draw(void); void Draw(void);
double GetDistance(Point2d mp); double GetDistance(Point2d mp);
void GenerateEdges(SEdgeList *el);
Vector GetReferencePos(void); Vector GetReferencePos(void);
void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index); void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index);

View File

@ -134,6 +134,7 @@ void vl(void); // debug function to validate heaps
#include "dsc.h" #include "dsc.h"
#include "polygon.h" #include "polygon.h"
#include "srf/surface.h"
class Entity; class Entity;
class hEntity; class hEntity;
@ -291,7 +292,7 @@ public:
// And the state that the caller must specify, determines where we // And the state that the caller must specify, determines where we
// render to and how // render to and how
hEntity entity; SPolyCurveList *polyCurves;
Vector origin, u, v; Vector origin, u, v;
int Getc(void); int Getc(void);
@ -307,7 +308,7 @@ public:
void Handle(int *dx, int x, int y, bool onCurve); void Handle(int *dx, int x, int y, bool onCurve);
void PlotCharacter(int *dx, int c, double spacing); void PlotCharacter(int *dx, int c, double spacing);
void PlotString(char *str, double spacing, void PlotString(char *str, double spacing,
hEntity he, Vector origin, Vector u, Vector v); SPolyCurveList *spcl, Vector origin, Vector u, Vector v);
Vector TransformIntPoint(int x, int y); Vector TransformIntPoint(int x, int y);
void LineSegment(int x0, int y0, int x1, int y1); void LineSegment(int x0, int y0, int x1, int y1);
@ -324,7 +325,7 @@ public:
void LoadAll(void); void LoadAll(void);
void PlotString(char *font, char *str, double spacing, void PlotString(char *font, char *str, double spacing,
hEntity he, Vector origin, Vector u, Vector v); SPolyCurveList *spcl, Vector origin, Vector u, Vector v);
}; };
class VectorFileWriter { class VectorFileWriter {

129
srf/ratpoly.cpp Normal file
View File

@ -0,0 +1,129 @@
#include "../solvespace.h"
double Bernstein(int k, int deg, double t)
{
switch(deg) {
case 1:
if(k == 0) {
return (1 - t);
} else if(k = 1) {
return t;
}
break;
case 2:
if(k == 0) {
return (1 - t)*(1 - t);
} else if(k == 1) {
return 2*(1 - t)*t;
} else if(k == 2) {
return t*t;
}
break;
case 3:
if(k == 0) {
return (1 - t)*(1 - t)*(1 - t);
} else if(k == 1) {
return 3*(1 - t)*(1 - t)*t;
} else if(k == 2) {
return 3*(1 - t)*t*t;
} else if(k == 3) {
return t*t*t;
}
break;
}
oops();
}
SPolyCurve SPolyCurve::From(Vector p0, Vector p1) {
SPolyCurve ret;
ZERO(&ret);
ret.deg = 1;
ret.weight[0] = ret.weight[1] = 1;
ret.ctrl[0] = p0;
ret.ctrl[1] = p1;
return ret;
}
SPolyCurve SPolyCurve::From(Vector p0, Vector p1, Vector p2) {
SPolyCurve ret;
ZERO(&ret);
ret.deg = 2;
ret.weight[0] = ret.weight[1] = ret.weight[2] = 1;
ret.ctrl[0] = p0;
ret.ctrl[1] = p1;
ret.ctrl[2] = p2;
return ret;
}
SPolyCurve SPolyCurve::From(Vector p0, Vector p1, Vector p2, Vector p3) {
SPolyCurve ret;
ZERO(&ret);
ret.deg = 3;
ret.weight[0] = ret.weight[1] = ret.weight[2] = ret.weight[3] = 1;
ret.ctrl[0] = p0;
ret.ctrl[1] = p1;
ret.ctrl[2] = p2;
ret.ctrl[3] = p3;
return ret;
}
Vector SPolyCurve::Start(void) {
return ctrl[0];
}
Vector SPolyCurve::Finish(void) {
return ctrl[deg];
}
Vector SPolyCurve::EvalAt(double t) {
Vector pt = Vector::From(0, 0, 0);
double d = 0;
int i;
for(i = 0; i <= deg; i++) {
double B = Bernstein(i, deg, t);
pt = pt.Plus(ctrl[i].ScaledBy(B*weight[i]));
d += weight[i]*B;
}
pt = pt.ScaledBy(1.0/d);
return pt;
}
void SPolyCurve::MakePwlInto(List<Vector> *l) {
l->Add(&(ctrl[0]));
MakePwlWorker(l, 0.0, 1.0);
}
void SPolyCurve::MakePwlWorker(List<Vector> *l, double ta, double tb) {
Vector pa = EvalAt(ta);
Vector pb = EvalAt(tb);
// Can't test in the middle, or certain cubics would break.
double tm1 = (2*ta + tb) / 3;
double tm2 = (ta + 2*tb) / 3;
Vector pm1 = EvalAt(tm1);
Vector pm2 = EvalAt(tm2);
double d = max(pm1.DistanceToLine(pa, pb.Minus(pa)),
pm2.DistanceToLine(pa, pb.Minus(pa)));
double tol = SS.chordTol/SS.GW.scale;
double step = 1.0/SS.maxSegments;
if((tb - ta) < step || d < tol) {
// A previous call has already added the beginning of our interval.
l->Add(&pb);
} else {
double tm = (ta + tb) / 2;
MakePwlWorker(l, ta, tm);
MakePwlWorker(l, tm, tb);
}
}
void SPolyCurveList::Clear(void) {
l.Clear();
}

View File

@ -2,8 +2,18 @@
#ifndef __SURFACE_H #ifndef __SURFACE_H
#define __SURFACE_H #define __SURFACE_H
class hSCurve; // Utility function
class hSSurface; double Bernstein(int k, int deg, double t);
class hSSurface {
public:
DWORD v;
};
class hSCurve {
public:
DWORD v;
};
// Stuff for rational polynomial curves, of degree one to three. These are // Stuff for rational polynomial curves, of degree one to three. These are
// our inputs. // our inputs.
@ -14,29 +24,30 @@ public:
double weight[4]; double weight[4];
Vector EvalAt(double t); Vector EvalAt(double t);
void GeneratePwlInto(SEdgeList *el); Vector Start(void);
Vector Finish(void);
void MakePwlInto(List<Vector> *l);
void MakePwlWorker(List<Vector> *l, double ta, double tb);
static SPolyCurve From(Vector p0, Vector p1, Vector p2, Vector p3); static SPolyCurve From(Vector p0, Vector p1, Vector p2, Vector p3);
static SPolyCurve From(Vector p0, Vector p1, Vector p2);
static SPolyCurve From(Vector p0, Vector p1); static SPolyCurve From(Vector p0, Vector p1);
}; };
class SPolyCurveList { class SPolyCurveList {
public: public:
List<SPolyCurve> l; List<SPolyCurve> l;
void Clear(void);
}; };
// Stuff for the surface trim curves: piecewise linear // Stuff for the surface trim curves: piecewise linear
class hSCurve {
public:
DWORD v;
};
class SCurve { class SCurve {
public: public:
hSCurve h; hSCurve h;
SList<Vector> pts; List<Vector> pts;
hSSurface srfA; hSSurface srfA;
hSSurface srfB; hSSurface srfB;
}; };
@ -53,11 +64,6 @@ public:
Vector out; Vector out;
}; };
class hSSurface {
public:
DWORD v;
};
class SSurface { class SSurface {
public: public:
hSSurface h; hSSurface h;
@ -66,7 +72,7 @@ public:
Vector ctrl[4][4]; Vector ctrl[4][4];
double weight[4][4]; double weight[4][4];
SList<STrimBy> trim; List<STrimBy> trim;
}; };
class SShell { class SShell {

57
ttf.cpp
View File

@ -20,7 +20,8 @@ void TtfFontList::LoadAll(void) {
} }
void TtfFontList::PlotString(char *font, char *str, double spacing, void TtfFontList::PlotString(char *font, char *str, double spacing,
hEntity he, Vector origin, Vector u, Vector v) SPolyCurveList *spcl,
Vector origin, Vector u, Vector v)
{ {
LoadAll(); LoadAll();
@ -29,15 +30,17 @@ void TtfFontList::PlotString(char *font, char *str, double spacing,
TtfFont *tf = &(l.elem[i]); TtfFont *tf = &(l.elem[i]);
if(strcmp(tf->FontFileBaseName(), font)==0) { if(strcmp(tf->FontFileBaseName(), font)==0) {
tf->LoadFontFromFile(false); tf->LoadFontFromFile(false);
tf->PlotString(str, spacing, he, origin, u, v); tf->PlotString(str, spacing, spcl, origin, u, v);
return; return;
} }
} }
// Couldn't find the font; so draw a big X for an error marker. // Couldn't find the font; so draw a big X for an error marker.
Entity *e = SS.GetEntity(he); SPolyCurve spc;
e->LineDrawOrGetDistanceOrEdge(origin, origin.Plus(u).Plus(v)); spc = SPolyCurve::From(origin, origin.Plus(u).Plus(v));
e->LineDrawOrGetDistanceOrEdge(origin.Plus(v), origin.Plus(u)); spcl->l.Add(&spc);
spc = SPolyCurve::From(origin.Plus(v), origin.Plus(u));
spcl->l.Add(&spc);
} }
@ -654,9 +657,10 @@ void TtfFont::PlotCharacter(int *dx, int c, double spacing) {
} }
void TtfFont::PlotString(char *str, double spacing, void TtfFont::PlotString(char *str, double spacing,
hEntity he, Vector porigin, Vector pu, Vector pv) SPolyCurveList *spcl,
Vector porigin, Vector pu, Vector pv)
{ {
entity = he; polyCurves = spcl;
u = pu; u = pu;
v = pv; v = pv;
origin = porigin; origin = porigin;
@ -685,42 +689,15 @@ Vector TtfFont::TransformIntPoint(int x, int y) {
} }
void TtfFont::LineSegment(int x0, int y0, int x1, int y1) { void TtfFont::LineSegment(int x0, int y0, int x1, int y1) {
Entity *e = SS.GetEntity(entity); SPolyCurve spc = SPolyCurve::From(TransformIntPoint(x0, y0),
e->LineDrawOrGetDistanceOrEdge(TransformIntPoint(x0, y0),
TransformIntPoint(x1, y1)); TransformIntPoint(x1, y1));
} polyCurves->l.Add(&spc);
Vector TtfFont::BezierEval(double t, Vector p0, Vector p1, Vector p2) {
return (p0.ScaledBy((1 - t)*(1 - t))).Plus(
(p1.ScaledBy(2*t*(1 - t))).Plus(
(p2.ScaledBy(t*t))));
}
void TtfFont::BezierPwl(double ta, double tb, Vector p0, Vector p1, Vector p2) {
Vector pa = BezierEval(ta, p0, p1, p2);
Vector pb = BezierEval(tb, p0, p1, p2);
double tm = (ta + tb) / 2;
Vector pm = BezierEval(tm, p0, p1, p2);
double tol = SS.chordTol/SS.GW.scale;
double step = 1.0/SS.maxSegments;
if((tb - ta) < step || pm.DistanceToLine(pa, pb.Minus(pa)) < tol) {
Entity *e = SS.GetEntity(entity);
e->LineDrawOrGetDistanceOrEdge(pa, pb);
} else {
BezierPwl(ta, tm, p0, p1, p2);
BezierPwl(tm, tb, p0, p1, p2);
}
} }
void TtfFont::Bezier(int x0, int y0, int x1, int y1, int x2, int y2) { void TtfFont::Bezier(int x0, int y0, int x1, int y1, int x2, int y2) {
Vector p0 = TransformIntPoint(x0, y0), SPolyCurve spc = SPolyCurve::From(TransformIntPoint(x0, y0),
p1 = TransformIntPoint(x1, y1), TransformIntPoint(x1, y1),
p2 = TransformIntPoint(x2, y2); TransformIntPoint(x2, y2));
polyCurves->l.Add(&spc);
BezierPwl(0, 1, p0, p1, p2);
} }