Caps. They don't work right yet (earcut problems?)

This commit is contained in:
Tony Garnock-Jones 2023-02-15 22:25:20 +01:00
parent dc45666ed7
commit 096946d0ad
4 changed files with 105 additions and 44 deletions

View File

@ -38,23 +38,54 @@
<sprite "t" []
<move <v -2.0 0.001 0.0>
<rotate <v 0.0 0.0 0.0>
<turtle [
90 U
90 L 0.05 B 90 R
PenDown 1 F 90 L 0.1 F 90 L 1 F 90 L 0.1 F DefinePen
Home
"double" SetSideOrientation
#t SetMiter
#f SetWireframe
90 L
PenDown
90 R 0 F
1 F 90 R 0 F
1 F 90 R 0 F
1 F 90 R 0 F
PenUp
]>
>>>
<texture ["textures/pixar128/wall/Rattan_pxr128.jpg"]
<turtle [
90 U
90 L 0.05 B 90 R
ClearPen
PenDown
[
30 to lean
1 lean cos / to len
lean R
[len F 90 lean + L 0.1 F 90 lean - L] 2 times
] apply
DefinePen GetPen to vPen
"DOUBLE" SetSideOrientation
"BOTH" SetCap
#t SetMiter
#f SetWireframe
[ to [f] PenDown f PenUp ] to draw
4 to nSides
360 nSides / to angle
20 nSides / to dist
[
angle R 0 F
[dist F angle R 0 F] nSides times
angle L
] to plotArea
Home
ClearPen
PenDown
90 D
plotArea
DefinePen GetPen to floorPen
Home
Home
[
floorPen SetPen
90 U [0.1 F] draw 90 D
vPen SetPen
quote plotArea draw 90 U 1 F 90 D
] 3 times
]>
>>>>
@<sprite "house" []
<move <v 0.0 0.0 10.0>

View File

@ -120,7 +120,7 @@
slen F
DefinePen
Home
"double" SetSideOrientation
"DOUBLE" SetSideOrientation
#t SetSmooth
PenDown
[6 CW dlen F] 240 times

View File

@ -234,6 +234,8 @@ export class VM<Self extends VM<Self>> {
}
}
export const D2R = Math.PI / 180;
export const Primitives: Environment<any> = {
'+'(a, b) { return [(a as number) + (b as number)]; },
'-'(a, b) { return [(a as number) - (b as number)]; },
@ -242,9 +244,9 @@ export const Primitives: Environment<any> = {
'%'(a, b) { return [(a as number) % (b as number)]; },
'neg'(v) { return [-(v as number)]; },
'cos'(n) { return [Math.cos(n as number)]; },
'sin'(n) { return [Math.sin(n as number)]; },
'tan'(n) { return [Math.tan(n as number)]; },
'cos'(n) { return [Math.cos(n as number * D2R)]; },
'sin'(n) { return [Math.sin(n as number * D2R)]; },
'tan'(n) { return [Math.tan(n as number * D2R)]; },
'to'() {
const n_or_ns = this.nextToken('to', (v: any): v is (symbol | symbol[]) =>

View File

@ -26,15 +26,13 @@ export class PenState {
clear() {
this.templatePath = [new Vector3()];
this.paths = null;
this.directions = null;
this.up();
}
set() {
if (!this.paths) throw new PenError("Cannot set pen with no paths");
this.templatePath = this.paths[0];
this.paths = null;
this.directions = null;
this.up();
}
down() {
@ -88,12 +86,18 @@ function pointDistance(origin: Vector3, direction: Vector3, plane: Plane): numbe
return (-plane.d - result2) / result1;
}
const D2R = Math.PI / 180;
export enum CapType {
NONE,
START,
END,
BOTH,
};
export class TurtleVM extends Cat.VM<TurtleVM> {
container: Mesh;
meshes: Mesh[] = [];
wireframe = false;
cap: CapType = CapType.BOTH;
counter = 0;
sideOrientation = Mesh.DEFAULTSIDE;
@ -125,12 +129,12 @@ export class TurtleVM extends Cat.VM<TurtleVM> {
rotate(y: number, x: number, z: number) {
const e = this.euler;
e.addInPlaceFromFloats(x * D2R, y * D2R, z * D2R);
e.addInPlaceFromFloats(x * Cat.D2R, y * Cat.D2R, z * Cat.D2R);
this.q = e.toQuaternion();
}
relativeRotate(y: number, x: number, z: number) {
this.q = this.q.multiply(Quaternion.FromEulerAngles(x * D2R, y * D2R, z * D2R));
this.q = this.q.multiply(Quaternion.FromEulerAngles(x * Cat.D2R, y * Cat.D2R, z * Cat.D2R));
}
penDown() {
@ -190,10 +194,6 @@ export class TurtleVM extends Cat.VM<TurtleVM> {
const uTotal: number[] = [];
const vTotal: number[] = [];
const capVerts = paths.flatMap(p => p[0].asArray());
console.log(capVerts);
console.log(earcut(capVerts, void 0, 3).map(i => paths[0][i].asArray()));
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) {
uTotal.push(0);
us.push([]);
@ -237,6 +237,21 @@ export class TurtleVM extends Cat.VM<TurtleVM> {
}
const computePointIndex = this.smooth ? cachedPoint : pushPoint;
function cap(stepIndex: number) {
const capVerts = paths.flatMap(p => p[stepIndex].asArray());
const capIndices = earcut(capVerts, void 0, 3);
for (let i = 0; i < capIndices.length; i += 3) {
indices.push(computePointIndex(capIndices[i+0], stepIndex),
computePointIndex(capIndices[i+1], stepIndex),
computePointIndex(capIndices[i+2], stepIndex));
}
}
if (this.cap === CapType.START || this.cap === CapType.BOTH) {
cap(0);
}
for (let pathIndex = 1; pathIndex < pathCount; pathIndex++) {
for (let stepIndex = 1; stepIndex < stepCount; stepIndex++) {
indices.push(computePointIndex(pathIndex - 1, stepIndex - 1),
@ -249,6 +264,10 @@ export class TurtleVM extends Cat.VM<TurtleVM> {
}
}
if (this.cap === CapType.END || this.cap === CapType.BOTH) {
cap(stepCount - 1);
}
VertexData.ComputeNormals(positions, indices, normals);
VertexData._ComputeSides(this.sideOrientation, positions, indices, normals, uvs);
@ -281,16 +300,16 @@ export const TurtlePrimitives: Cat.Environment<TurtleVM> = Object.assign({}, Cat
return [];
},
'GetHeading'() { return [this.euler.asArray().map(v => v / D2R)]; },
'GetRX'() { return [this.euler.x / D2R]; },
'GetRY'() { return [this.euler.y / D2R]; },
'GetRZ'() { return [this.euler.z / D2R]; },
'SetRX'(v) { this.q.x = v as number * D2R; return []; },
'SetRY'(v) { this.q.y = v as number * D2R; return []; },
'SetRZ'(v) { this.q.z = v as number * D2R; return []; },
'GetHeading'() { return [this.euler.asArray().map(v => v / Cat.D2R)]; },
'GetRX'() { return [this.euler.x / Cat.D2R]; },
'GetRY'() { return [this.euler.y / Cat.D2R]; },
'GetRZ'() { return [this.euler.z / Cat.D2R]; },
'SetRX'(v) { this.q.x = v as number * Cat.D2R; return []; },
'SetRY'(v) { this.q.y = v as number * Cat.D2R; return []; },
'SetRZ'(v) { this.q.z = v as number * Cat.D2R; return []; },
'SetHeading'(v) {
const [x, y, z] = v as number[];
this.q = Quaternion.FromEulerAngles(x as number * D2R, y as number * D2R, z as number * D2R);
this.q = Quaternion.FromEulerAngles(x as number * Cat.D2R, y as number * Cat.D2R, z as number * Cat.D2R);
return [];
},
@ -328,15 +347,24 @@ export const TurtlePrimitives: Cat.Environment<TurtleVM> = Object.assign({}, Cat
'PenUp'() { this.penUp(false); return []; },
'Close'() { this.penUp(true); return []; },
'SetCap'(t) {
const n = t as keyof typeof CapType;
if (typeof t === 'string' && n in CapType) {
this.cap = CapType[n];
} else {
throw new Cat.TypeError("Bad cap type: " + t);
}
return [];
},
'SetWireframe'(b) { this.wireframe = b as boolean; return []; },
'SetSmooth'(b) { this.smooth = b as boolean; return []; },
'SetMiter'(b) { this.miter = b as boolean; return []; },
'SetSideOrientation'(s) {
switch (s) {
case "default": this.sideOrientation = Mesh.DEFAULTSIDE; break;
case "front": this.sideOrientation = Mesh.FRONTSIDE; break;
case "back": this.sideOrientation = Mesh.BACKSIDE; break;
case "double": this.sideOrientation = Mesh.DOUBLESIDE; break;
case "DEFAULT": this.sideOrientation = Mesh.DEFAULTSIDE; break;
case "FRONT": this.sideOrientation = Mesh.FRONTSIDE; break;
case "BACK": this.sideOrientation = Mesh.BACKSIDE; break;
case "DOUBLE": this.sideOrientation = Mesh.DOUBLESIDE; break;
default: throw new TypeError("Invalid SideOrientation: " + s);
}
return [];