Skip to content

Commit 35381cf

Browse files
committed
Outer fillet on outline working
1 parent 21088e8 commit 35381cf

1 file changed

Lines changed: 144 additions & 120 deletions

File tree

src/main/java/eu/mihosoft/vrl/v3d/Fillet.java

Lines changed: 144 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public class Fillet extends Primitive {
1010

1111
double w, h;
1212
private static final double numArcPoints = 12;
13+
private static double filletOfset=0.001;
1314
/** The properties. */
1415
private final PropertyStorage properties = new PropertyStorage();
1516

@@ -30,46 +31,46 @@ public Fillet(double w, double h) {
3031
}
3132

3233
public static CSG corner(double rad, double angle) throws ColinearPointsException {
33-
// --- Build the concave quarter-circle fillet profile ---
34-
//
35-
// Shape (in XY plane, swept around Z):
36-
//
37-
// (0,rad) ──arc─── (rad,rad)
38-
// center
39-
// at (rad,rad)
40-
// (0,0) ────────── (rad,0)
41-
//
42-
// The arc is concave (bites inward), matching what the
43-
// old Fillet+Sphere difference was carving out.
44-
45-
46-
List<Vector3d> pts = new ArrayList<>();
47-
48-
// Corner at origin
49-
pts.add(new Vector3d(0, 0, 0));
50-
51-
// Straight edge along +X
52-
pts.add(new Vector3d(rad, 0, 0));
53-
54-
// Concave quarter-circle arc: center=(rad,rad), radius=rad
55-
// sweeps from 270° → 180° (i.e. (rad,0) → (0,rad))
56-
for (int i = 0; i <= numArcPoints; i++) {
57-
double a = Math.toRadians(270.0 - 90.0 * i / numArcPoints);
58-
double x = rad + rad * Math.cos(a);
59-
double y = rad + rad * Math.sin(a);
60-
pts.add(new Vector3d(x, y, 0));
61-
}
62-
63-
// Straight edge back down to origin closes the polygon
64-
// (fromPoints auto-closes, so no need to re-add (0,0,0))
65-
66-
Polygon profile = Polygon.fromPoints(pts);
67-
68-
// --- Sweep the profile around the Z axis ---
69-
// radius=0 → profile is already positioned relative to the axis
70-
// z=0 → no axial offset
71-
// steps=32 → match arc resolution for a smooth result
72-
return Extrude.sweep(profile, angle/numArcPoints, 0, 0, (int)numArcPoints).roty(90);
34+
// --- Build the concave quarter-circle fillet profile ---
35+
//
36+
// Shape (in XY plane, swept around Z):
37+
//
38+
// (0,rad) ──arc─── (rad,rad)
39+
// center │
40+
// at (rad,rad) │
41+
// (0,0) ────────── (rad,0)
42+
//
43+
// The arc is concave (bites inward), matching what the
44+
// old Fillet+Sphere difference was carving out.
45+
46+
List<Vector3d> pts = new ArrayList<>();
47+
48+
// Corner at origin
49+
pts.add(new Vector3d(0, -filletOfset, 0));
50+
51+
// Straight edge along +X
52+
pts.add(new Vector3d(rad, -filletOfset, 0));
53+
54+
// Concave quarter-circle arc: center=(rad,rad), radius=rad
55+
// sweeps from 270° → 180° (i.e. (rad,0) → (0,rad))
56+
for (int i = 0; i <= numArcPoints; i++) {
57+
double a = Math.toRadians(270.0 - 90.0 * i / numArcPoints);
58+
double x = rad + rad * Math.cos(a);
59+
double y = rad + rad * Math.sin(a);
60+
pts.add(new Vector3d(x, y, 0));
61+
}
62+
63+
// Straight edge back down to origin closes the polygon
64+
// (fromPoints auto-closes, so no need to re-add (0,0,0))
65+
66+
Polygon profile = Polygon.fromPoints(pts);
67+
68+
// --- Sweep the profile around the Z axis ---
69+
// radius=0 → profile is already positioned relative to the axis
70+
// z=0 → no axial offset
71+
// steps=32 → match arc resolution for a smooth result
72+
return Extrude.sweep(profile, angle / numArcPoints, 0, 0, (int) numArcPoints)
73+
.roty(90);
7374
}
7475

7576
public static CSG outerFillet(CSG base, double rad) throws ColinearPointsException {
@@ -79,85 +80,106 @@ public static CSG outerFillet(CSG base, double rad) throws ColinearPointsExcepti
7980

8081
public static CSG outerFillet(List<Polygon> polys, double rad) {
8182

82-
ArrayList<CSG> parts = new ArrayList<>();
83-
84-
for (Polygon p : polys) {
85-
boolean isHole = false;
86-
try {
87-
isHole = !Extrude.isCCW(p);
88-
} catch (ColinearPointsException e) {
89-
e.printStackTrace();
90-
}
91-
System.err.println("Polygon filler " + (isHole ? "hole" : "outside"));
92-
93-
int size = p.getVertices().size();
94-
for (int i = 0; i < size; i++) {
95-
int next = (i + 1) % size;
96-
int nextNext = (next + 1) % size;
97-
98-
Vector3d position0 = p.getVertices().get(i).pos;
99-
Vector3d position1 = p.getVertices().get(next).pos; // corner vertex
100-
Vector3d position2 = p.getVertices().get(nextNext).pos;
101-
102-
Vector3d seg1 = position0.minus(position1); // incoming edge
103-
Vector3d seg2 = position2.minus(position1); // outgoing edge
104-
105-
double len = seg1.magnitude();
106-
double theta = Math.toDegrees(seg1.angle(seg2));
107-
double crossZ = seg1.cross(seg2).z;
108-
109-
boolean isConvex = isHole ? (crossZ > 0) : (crossZ < 0);
110-
boolean isReflex = !isConvex;
111-
112-
double filletAngle = 180.0 - theta;
113-
114-
// --- Absolute rotation for the corner (perpendicular to seg1) ---
115-
double cornerAngleAbs = Math.toDegrees(seg1.angle(Vector3d.Y_ONE));
116-
if (seg1.x < 0)
117-
cornerAngleAbs = 360 - cornerAngleAbs;
118-
if (isHole) cornerAngleAbs += 180;
119-
if (isReflex) cornerAngleAbs += 180;
120-
121-
// --- Absolute rotation for the edge fillet (parallel to seg1) ---
122-
double edgeAngleAbs = Math.toDegrees(seg1.angle(Vector3d.Y_ONE));
123-
if (seg1.x < 0)
124-
edgeAngleAbs = 360 - edgeAngleAbs;
125-
if (isHole) {
126-
edgeAngleAbs += 180;
127-
128-
}
129-
System.err.println(
130-
" vertex=" + next +
131-
" theta=" + String.format("%.2f", theta) +
132-
" filletAngle=" + String.format("%.2f", filletAngle) +
133-
" cornerAbs=" + String.format("%.2f", cornerAngleAbs) +
134-
" edgeAbs=" + String.format("%.2f", edgeAngleAbs) +
135-
" convex=" + isConvex +
136-
" reflex=" + isReflex
137-
);
138-
139-
// --- Straight edge fillet ---
140-
// Runs from position1 toward position0 (along seg1 direction)
141-
// toYMax() aligns the fillet profile to sit flush on the face
142-
CSG edgeFillet = new Fillet(rad, len).toCSG().toYMax()
143-
.rotz(edgeAngleAbs)
144-
.move(position1);
145-
//parts.add(edgeFillet);
146-
147-
// --- Corner fillet ---
148-
if (filletAngle > 0.01 && filletAngle < 359.99) {
149-
try {
150-
parts.add(corner(rad, filletAngle)
151-
.rotz(cornerAngleAbs)
152-
.move(position1));
153-
} catch (ColinearPointsException e) {
154-
e.printStackTrace();
155-
}
156-
}
157-
}
158-
}
159-
return CSG.unionAll(parts);
83+
boolean outer = true;
84+
85+
ArrayList<CSG> parts = new ArrayList<>();
86+
87+
for (Polygon p : polys) {
88+
boolean isHole = false;
89+
try {
90+
isHole = !Extrude.isCCW(p);
91+
} catch (ColinearPointsException e) {
92+
e.printStackTrace();
93+
}
94+
if(!outer)
95+
isHole=!isHole;
96+
System.err.println("Polygon filler " + (isHole ? "hole" : "outside"));
97+
98+
int size = p.getVertices().size();
99+
for (int i = 0; i < size; i++) {
100+
int next = (i + 1) % size;
101+
int nextNext = (next + 1) % size;
102+
103+
Vector3d position0 = p.getVertices().get(i).pos;
104+
Vector3d position1 = p.getVertices().get(next).pos; // corner vertex
105+
Vector3d position2 = p.getVertices().get(nextNext).pos;
106+
107+
Vector3d seg1 = position0.minus(position1); // incoming, pointing away from corner
108+
Vector3d seg2 = position2.minus(position1); // outgoing, pointing away from corner
109+
110+
double len = seg1.magnitude();
111+
double theta = Math.toDegrees(seg1.angle(seg2)); // always 0–180
112+
double crossZ = seg1.cross(seg2).z;
113+
114+
// Convex vs reflex meaning depends on winding:
115+
// Outside (CCW): convex corners have crossZ < 0
116+
// Hole (CW): convex corners have crossZ > 0
117+
boolean isConvex = isHole ? (crossZ > 0) : (crossZ < 0);
118+
boolean isReflex = !isConvex;
119+
120+
// filletAngle = exterior sweep angle of the corner piece
121+
double filletAngle = 180.0 - theta;
122+
123+
124+
// Base rotation: align to seg1 direction relative to Y axis
125+
double baseAngle = Math.toDegrees(seg1.angle(Vector3d.Y_ONE));
126+
if (seg1.x < 0)
127+
baseAngle = 360 - baseAngle;
128+
double edgeAngleAbs = baseAngle;
129+
if (isHole)
130+
edgeAngleAbs += 180;
131+
if(isReflex) {
132+
filletAngle=180-filletAngle;
133+
if(filletAngle<90)
134+
filletAngle=0;
135+
baseAngle+=90;
136+
}
137+
// -------------------------------------------------------
138+
// Edge fillet: always placed, runs along seg1 from position1
139+
// Holes face inward so flip 180°
140+
// -------------------------------------------------------
141+
142+
143+
CSG edgeFillet = new Fillet(rad, len).toCSG();
144+
if(isHole)
145+
edgeFillet = edgeFillet.toYMax();
146+
else
147+
edgeFillet = edgeFillet.toYMin().mirrorx();
148+
if(!outer)
149+
edgeFillet = edgeFillet.mirrorx();
150+
edgeFillet = edgeFillet.rotz(edgeAngleAbs).move(position1);
151+
parts.add(edgeFillet);
152+
double cornerAngleAbs = baseAngle;
153+
if(!isHole && outer) {
154+
cornerAngleAbs=cornerAngleAbs-filletAngle-90;
155+
}
156+
// -------------------------------------------------------
157+
// Corner fillet: only placed on REFLEX corners
158+
//
159+
// Outside reflex: edge fillets don't reach into the notch,
160+
// corner piece fills it, no extra rotation.
161+
//
162+
// Hole reflex: edge fillets each consume 90° at the corner,
163+
// so the corner piece must start 90° further
164+
// around to align with where they leave off.
165+
// -------------------------------------------------------
166+
if (filletAngle > 0.01 && filletAngle < 359.99) {
167+
168+
// Outside: no extra offset — logic is reversed (edge fillets
169+
// run away from the corner, so no 90° consumption occurs)
170+
171+
172+
try {
173+
parts.add(corner(rad, filletAngle).rotz(cornerAngleAbs).move(position1));
174+
} catch (ColinearPointsException e) {
175+
e.printStackTrace();
176+
}
177+
}
178+
}
179+
}
180+
return CSG.unionAll(parts);
160181
}
182+
161183
@Override
162184
public CSG toCSG() {
163185
// --- 1. Build the fillet cross-section polygon in the XZ plane (Y = 0) ---
@@ -173,15 +195,17 @@ public CSG toCSG() {
173195
List<Vector3d> profilePoints = new ArrayList<>();
174196

175197
// Inner corner
176-
profilePoints.add(new Vector3d(0, 0, 0));
198+
profilePoints.add(new Vector3d(-filletOfset, 0, 0));
199+
//profilePoints.add(new Vector3d(0, 0, 0));
177200

178201
// Concave quarter-circle arc
179202
for (int i = 0; i <= numArcPoints; i++) {
180203
double angle = Math.toRadians(270.0 - 90.0 * i / numArcPoints);
181204
double x = w + w * Math.cos(angle);
182205
double z = w + w * Math.sin(angle);
183-
profilePoints.add(0,new Vector3d(x, 0, z));
206+
profilePoints.add(0, new Vector3d(x, 0, z));
184207
}
208+
profilePoints.add(new Vector3d(-filletOfset, 0, w));
185209
// Last arc point lands exactly at (0, 0, w); polygon closes to (0,0,0)
186210
try {
187211
Polygon profile = Polygon.fromPoints(profilePoints);

0 commit comments

Comments
 (0)