Wireframe, and fixes to mitering

This commit is contained in:
Tony Garnock-Jones 2023-02-15 17:06:27 +01:00
parent 5606b3c2a4
commit dc45666ed7
2 changed files with 94 additions and 16 deletions

View File

@ -35,7 +35,28 @@
<scale <v 2.0 2.0 0.1>
<box>>>>>>
<sprite "house" []
<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
]>
>>>
@<sprite "house" []
<move <v 0.0 0.0 10.0>
<rotate <v 0.0 0.0 0.0>
<texture ["textures/oak-herringbone-5e80fb40b00c9-1200.jpg"]
@ -82,13 +103,16 @@
[ to [angle length] [ angle L [length F] draw ] saved ] to wall
#f SetWireframe
#t SetMiter
[ 0 F ] to miter
[ 90 L miter ] to --
[ 90 R miter ] to ++
exteriorWall
--
[
++
2.1 F --
0.3 F [1 F] eDoor 0.9 F ++
7.7 F ++

View File

@ -1,5 +1,6 @@
import {
Mesh,
MeshBuilder,
Plane,
Quaternion,
Ray,
@ -9,6 +10,7 @@ import {
} from '@babylonjs/core/Legacy/legacy';
import * as Cat from './cat.js';
import { earcut } from './earcut';
export class PenError extends Cat.RuntimeError {}
@ -40,6 +42,11 @@ export class PenState {
this.directions = [];
}
up() {
this.paths = null;
this.directions = null;
}
push(pos: Vector3, q: Quaternion, miter: boolean) {
const directions = this.directions!;
const paths = this.paths!;
@ -53,23 +60,40 @@ export class PenState {
for (let pathIndex = 0; pathIndex < paths.length; pathIndex++) {
const steps = paths[pathIndex];
const p = steps[steps.length - 1];
steps[steps.length - 1] = p.add(lastDir.scale(new Ray(p, lastDir).intersectsPlane(miterPlane)!));
const d = pointDistance(p, lastDir, miterPlane);
if (d !== null) steps[steps.length - 1] = p.add(lastDir.scale(d));
}
this.templatePath.forEach((p, i) => {
const r = p.multiply(this.templateScale).applyRotationQuaternion(q).addInPlace(pos);
const d = pointDistance(r, thisDir, miterPlane);
if (d !== null) r.addInPlace(thisDir.scale(d));
paths[i].push(r);
});
} else {
this.templatePath.forEach((p, i) => {
const r = p.multiply(this.templateScale).applyRotationQuaternion(q).addInPlace(pos);
paths[i].push(r);
});
}
this.templatePath.forEach((p, i) => {
paths[i].push(p.multiply(this.templateScale).applyRotationQuaternion(q).addInPlace(pos));
});
directions.push(q);
}
};
// Adapted from Ray.intersectsPlane
function pointDistance(origin: Vector3, direction: Vector3, plane: Plane): number | null {
const result1 = Vector3.Dot(plane.normal, direction);
if (Math.abs(result1) < 1e-6) return null; // direction parallel to plane
const result2 = Vector3.Dot(plane.normal, origin);
return (-plane.d - result2) / result1;
}
const D2R = Math.PI / 180;
export class TurtleVM extends Cat.VM<TurtleVM> {
container: Mesh;
meshes: Mesh[] = [];
wireframe = false;
counter = 0;
sideOrientation = Mesh.DEFAULTSIDE;
@ -121,10 +145,37 @@ export class TurtleVM extends Cat.VM<TurtleVM> {
throw new Error('todo');
}
const m = new Mesh(this.container.name + this.counter++);
// TODO: this.sideOrientation
if (this.wireframe) {
this.emitWireframe();
} else {
this.emitSolid();
}
const vertexData = new VertexData();
this.pen.up();
}
emitWireframe() {
const lines: Vector3[][] = [];
const paths = this.pen.paths!;
const pathCount = paths.length;
const stepCount = paths[0].length;
for (let pathIndex = 1; pathIndex < pathCount; pathIndex++) {
for (let stepIndex = 1; stepIndex < stepCount; stepIndex++) {
lines.push([paths[pathIndex - 1][stepIndex - 1],
paths[pathIndex - 1][stepIndex],
paths[pathIndex][stepIndex],
paths[pathIndex][stepIndex - 1]]);
}
}
const meshName = this.container.name + this.counter++;
const ls = MeshBuilder.CreateLineSystem(meshName, { lines }, null);
ls.parent = this.container;
this.meshes.push(ls);
}
emitSolid() {
const positions: number[] = [];
const indices: number[] = [];
const normals: number[] = [];
@ -139,6 +190,10 @@ 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([]);
@ -167,7 +222,8 @@ export class TurtleVM extends Cat.VM<TurtleVM> {
const pointIndex = positions.length / 3;
const p = paths[pathIndex][stepIndex];
positions.push(... p.asArray());
uvs.push((p.x - p.z) / 1.2, (p.y) / 0.9);
uvs.push((p.x - p.z), (p.y));
// uvs.push((p.x - p.z) / 1.2, (p.y) / 0.9);
// uvs.push(us[pathIndex][stepIndex] / uTotal[pathIndex],
// vs[stepIndex][pathIndex] / vTotal[stepIndex]);
return pointIndex;
@ -195,18 +251,15 @@ export class TurtleVM extends Cat.VM<TurtleVM> {
VertexData.ComputeNormals(positions, indices, normals);
VertexData._ComputeSides(this.sideOrientation, positions, indices, normals, uvs);
const vertexData = new VertexData();
vertexData.positions = new Float32Array(positions);
vertexData.indices = new Int32Array(indices);
vertexData.normals = new Float32Array(normals);
vertexData.uvs = new Float32Array(uvs);
const m = new Mesh(this.container.name + this.counter++);
vertexData.applyToMesh(m);
// const m = MeshBuilder.CreateRibbon(this.container.name + this.counter++, {
// pathArray: this.pen.paths!,
// sideOrientation: this.sideOrientation,
// });
m.parent = this.container;
this.meshes.push(m);
}
@ -275,6 +328,7 @@ export const TurtlePrimitives: Cat.Environment<TurtleVM> = Object.assign({}, Cat
'PenUp'() { this.penUp(false); return []; },
'Close'() { this.penUp(true); 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) {