solvespace/groupmesh.cpp
Jonathan Westhues 07ddd62a3a Preparatory work for Boolean. Make the u and v coordinates of the
trim curves for all surfaces lie between 0 and 1. And add routines
to merge the curves and surfaces from two shells into one, and to
split the trim curves into their piecewise linear segments and then
reassemble them into trim curves.

[git-p4: depot-paths = "//depot/solvespace/": change = 1905]
2009-01-25 03:52:29 -08:00

300 lines
9.3 KiB
C++

#include "solvespace.h"
#define gs (SS.GW.gs)
bool Group::AssembleLoops(void) {
SBezierList sbl;
ZERO(&sbl);
int i;
for(i = 0; i < SS.entity.n; i++) {
Entity *e = &(SS.entity.elem[i]);
if(e->group.v != h.v) continue;
if(e->construction) continue;
e->GenerateBezierCurves(&sbl);
}
bool allClosed;
bezierLoopSet = SBezierLoopSet::From(&sbl, &poly,
&allClosed, &(polyError.notClosedAt));
sbl.Clear();
return allClosed;
}
void Group::GenerateLoops(void) {
poly.Clear();
bezierLoopSet.Clear();
if(type == DRAWING_3D || type == DRAWING_WORKPLANE ||
type == ROTATE || type == TRANSLATE || type == IMPORTED)
{
if(AssembleLoops()) {
polyError.how = POLY_GOOD;
if(!poly.AllPointsInPlane(&(polyError.errorPointAt))) {
// The edges aren't all coplanar; so not a good polygon
polyError.how = POLY_NOT_COPLANAR;
poly.Clear();
bezierLoopSet.Clear();
}
if(poly.SelfIntersecting(&(polyError.errorPointAt))) {
polyError.how = POLY_SELF_INTERSECTING;
poly.Clear();
bezierLoopSet.Clear();
}
} else {
polyError.how = POLY_NOT_CLOSED;
poly.Clear();
bezierLoopSet.Clear();
}
}
}
void Group::GenerateShellForStepAndRepeat(void) {
Group *src = SS.GetGroup(opA);
SShell *srcs = &(src->thisShell); // the shell to step and repeat
int n = (int)valA, a0 = 0;
if(subtype == ONE_SIDED && skipFirst) {
a0++; n++;
}
int a;
for(a = a0; a < n; a++) {
int ap = a*2 - (subtype == ONE_SIDED ? 0 : (n-1));
int remap = (a == (n - 1)) ? REMAP_LAST : a;
if(type == TRANSLATE) {
Vector trans = Vector::From(h.param(0), h.param(1), h.param(2));
trans = trans.ScaledBy(ap);
} else {
Vector trans = Vector::From(h.param(0), h.param(1), h.param(2));
double theta = ap * SS.GetParam(h.param(3))->val;
double c = cos(theta), s = sin(theta);
Vector axis = Vector::From(h.param(4), h.param(5), h.param(6));
Quaternion q = Quaternion::From(c, s*axis.x, s*axis.y, s*axis.z);
}
if(src->meshCombine == COMBINE_AS_DIFFERENCE) {
} else {
}
}
}
void Group::GenerateShellAndMesh(void) {
thisShell.Clear();
if(type == TRANSLATE || type == ROTATE) {
GenerateShellForStepAndRepeat();
goto done;
}
if(type == EXTRUDE) {
Group *src = SS.GetGroup(opA);
Vector translate = Vector::From(h.param(0), h.param(1), h.param(2));
Vector tbot, ttop;
if(subtype == ONE_SIDED) {
tbot = Vector::From(0, 0, 0); ttop = translate.ScaledBy(2);
} else {
tbot = translate.ScaledBy(-1); ttop = translate.ScaledBy(1);
}
thisShell.MakeFromExtrusionOf(&(src->bezierLoopSet), tbot, ttop, color);
} else if(type == LATHE) {
Group *src = SS.GetGroup(opA);
Vector orig = SS.GetEntity(predef.origin)->PointGetNum();
Vector axis = SS.GetEntity(predef.entityB)->VectorGetNum();
axis = axis.WithMagnitude(1);
} else if(type == IMPORTED) {
// Triangles are just copied over, with the appropriate transformation
// applied.
Vector offset = {
SS.GetParam(h.param(0))->val,
SS.GetParam(h.param(1))->val,
SS.GetParam(h.param(2))->val };
Quaternion q = {
SS.GetParam(h.param(3))->val,
SS.GetParam(h.param(4))->val,
SS.GetParam(h.param(5))->val,
SS.GetParam(h.param(6))->val };
for(int i = 0; i < impMesh.l.n; i++) {
STriangle st = impMesh.l.elem[i];
if(st.meta.face != 0) {
hEntity he = { st.meta.face };
st.meta.face = Remap(he, 0).v;
}
st.a = q.Rotate(st.a).Plus(offset);
st.b = q.Rotate(st.b).Plus(offset);
st.c = q.Rotate(st.c).Plus(offset);
}
}
runningMesh.Clear();
runningShell.Clear();
// If this group contributes no new mesh, then our running mesh is the
// same as last time, no combining required. Likewise if we have a mesh
// but it's suppressed.
if(suppress) {
runningShell.MakeFromCopyOf(PreviousGroupShell());
goto done;
}
// So our group's mesh appears in thisMesh. Combine this with the previous
// group's mesh, using the requested operation.
bool prevMeshError = meshError.yes;
meshError.yes = false;
meshError.interferesAt.Clear();
SShell *a = PreviousGroupShell();
if(meshCombine == COMBINE_AS_UNION) {
runningShell.MakeFromUnionOf(a, &thisShell);
} else if(meshCombine == COMBINE_AS_DIFFERENCE) {
runningShell.MakeFromDifferenceOf(a, &thisShell);
} else {
if(0) //&(meshError.interferesAt)
{
meshError.yes = true;
// And the list of failed triangles goes in meshError.interferesAt
}
}
if(prevMeshError != meshError.yes) {
// The error is reported in the text window for the group.
SS.later.showTW = true;
}
done:
runningShell.TriangulateInto(&runningMesh);
emphEdges.Clear();
if(h.v == SS.GW.activeGroup.v && SS.edgeColor != 0) {
runningShell.MakeEdgesInto(&emphEdges);
}
}
SShell *Group::PreviousGroupShell(void) {
int i;
for(i = 0; i < SS.group.n; i++) {
Group *g = &(SS.group.elem[i]);
if(g->h.v == h.v) break;
}
if(i == 0 || i >= SS.group.n) oops();
return &(SS.group.elem[i-1].runningShell);
}
void Group::Draw(void) {
// Show this even if the group is not visible. It's already possible
// to show or hide just this with the "show solids" flag.
int specColor;
if(type == DRAWING_3D || type == DRAWING_WORKPLANE) {
specColor = RGB(25, 25, 25); // force the color to something dim
} else {
specColor = -1; // use the model color
}
// The back faces are drawn in red; should never seem them, since we
// draw closed shells, so that's a debugging aid.
GLfloat mpb[] = { 1.0f, 0.1f, 0.1f, 1.0 };
glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, mpb);
// When we fill the mesh, we need to know which triangles are selected
// or hovered, in order to draw them differently.
DWORD mh = 0, ms1 = 0, ms2 = 0;
hEntity he = SS.GW.hover.entity;
if(he.v != 0 && SS.GetEntity(he)->IsFace()) {
mh = he.v;
}
SS.GW.GroupSelection();
if(gs.faces > 0) ms1 = gs.face[0].v;
if(gs.faces > 1) ms2 = gs.face[1].v;
if(SS.GW.showShaded) {
glEnable(GL_LIGHTING);
glxFillMesh(specColor, &runningMesh, mh, ms1, ms2);
glDisable(GL_LIGHTING);
glLineWidth(1);
glxDepthRangeOffset(2);
glxColor3d(REDf (SS.edgeColor),
GREENf(SS.edgeColor),
BLUEf (SS.edgeColor));
glxDrawEdges(&emphEdges);
}
if(meshError.yes) {
// Draw the error triangles in bright red stripes, with no Z buffering
GLubyte mask[32*32/8];
memset(mask, 0xf0, sizeof(mask));
glPolygonStipple(mask);
int specColor = 0;
glDisable(GL_DEPTH_TEST);
glColor3d(0, 0, 0);
glxFillMesh(0, &meshError.interferesAt, 0, 0, 0);
glEnable(GL_POLYGON_STIPPLE);
glColor3d(1, 0, 0);
glxFillMesh(0, &meshError.interferesAt, 0, 0, 0);
glEnable(GL_DEPTH_TEST);
glDisable(GL_POLYGON_STIPPLE);
}
if(SS.GW.showMesh) glxDebugMesh(&runningMesh);
// And finally show the polygons too
if(!SS.GW.showShaded) return;
if(polyError.how == POLY_NOT_CLOSED) {
// Report this error only in sketch-in-workplane groups; otherwise
// it's just a nuisance.
if(type == DRAWING_WORKPLANE) {
glDisable(GL_DEPTH_TEST);
glxColor4d(1, 0, 0, 0.2);
glLineWidth(10);
glBegin(GL_LINES);
glxVertex3v(polyError.notClosedAt.a);
glxVertex3v(polyError.notClosedAt.b);
glEnd();
glLineWidth(1);
glxColor3d(1, 0, 0);
glPushMatrix();
glxTranslatev(polyError.notClosedAt.b);
glxOntoWorkplane(SS.GW.projRight, SS.GW.projUp);
glxWriteText("not closed contour!");
glPopMatrix();
glEnable(GL_DEPTH_TEST);
}
} else if(polyError.how == POLY_NOT_COPLANAR ||
polyError.how == POLY_SELF_INTERSECTING)
{
// These errors occur at points, not lines
if(type == DRAWING_WORKPLANE) {
glDisable(GL_DEPTH_TEST);
glxColor3d(1, 0, 0);
glPushMatrix();
glxTranslatev(polyError.errorPointAt);
glxOntoWorkplane(SS.GW.projRight, SS.GW.projUp);
if(polyError.how == POLY_NOT_COPLANAR) {
glxWriteText("points not all coplanar!");
} else {
glxWriteText("contour is self-intersecting!");
}
glPopMatrix();
glEnable(GL_DEPTH_TEST);
}
} else {
glxColor4d(0, 0.1, 0.1, 0.5);
glxDepthRangeOffset(1);
glxFillPolygon(&poly);
glxDepthRangeOffset(0);
}
}