Checkpoint before book geometry fixes
This commit is contained in:
@@ -68,11 +68,12 @@ const OPEN_SEAM_GAP = 0.003;
|
|||||||
const PAGE_COUNT_MIN = 40;
|
const PAGE_COUNT_MIN = 40;
|
||||||
const PAGE_COUNT_STEP = 10;
|
const PAGE_COUNT_STEP = 10;
|
||||||
const PAGE_WIDTH = 1.62;
|
const PAGE_WIDTH = 1.62;
|
||||||
|
const PAGE_SPLINE_LENGTH = 1.955;
|
||||||
|
const PAGE_LINE_SEGMENTS = 24;
|
||||||
const PAGE_DEPTH = 2.24;
|
const PAGE_DEPTH = 2.24;
|
||||||
const COVER_OVERHANG = 0.13;
|
const COVER_OVERHANG = 0.13;
|
||||||
const COVER_SUPPORT_OVERHANG = 0.055;
|
const COVER_SUPPORT_OVERHANG = 0.055;
|
||||||
const HINGE_INSET = 0.07;
|
const HINGE_INSET = 0.07;
|
||||||
const MAX_SUPPORTED_STACK_RISE = PAGE_WIDTH * 0.5;
|
|
||||||
const SUPPORT_ANGLE_STEPS = 720;
|
const SUPPORT_ANGLE_STEPS = 720;
|
||||||
const SUPPORT_ANGLE_CANDIDATES = Array.from({ length: SUPPORT_ANGLE_STEPS }, (_, sample) => {
|
const SUPPORT_ANGLE_CANDIDATES = Array.from({ length: SUPPORT_ANGLE_STEPS }, (_, sample) => {
|
||||||
const angle = sample / SUPPORT_ANGLE_STEPS * Math.PI * 2;
|
const angle = sample / SUPPORT_ANGLE_STEPS * Math.PI * 2;
|
||||||
@@ -208,17 +209,19 @@ function rebuildBook() {
|
|||||||
const coverDepth = 2.30;
|
const coverDepth = 2.30;
|
||||||
const coverThickness = BOOK_PROFILE.coverThickness;
|
const coverThickness = BOOK_PROFILE.coverThickness;
|
||||||
const pageWidth = PAGE_WIDTH;
|
const pageWidth = PAGE_WIDTH;
|
||||||
|
const pageSplineLength = PAGE_SPLINE_LENGTH;
|
||||||
const pageDepth = PAGE_DEPTH;
|
const pageDepth = PAGE_DEPTH;
|
||||||
const bundleCount = Math.max(4, Math.round(pageCount / 10));
|
const bundleCount = Math.max(4, Math.round(pageCount / 10));
|
||||||
const spineWidth = calculateSpineWidth(bundleCount);
|
const spineWidth = calculateSpineWidth(bundleCount);
|
||||||
const leftCount = calculateLeftBundleCount(bundleCount);
|
const leftCount = calculateLeftBundleCount(bundleCount);
|
||||||
const hingeX = spineWidth * 0.5 + HINGE_INSET;
|
const hingeX = spineWidth * 0.5 + HINGE_INSET;
|
||||||
|
const foreEdgeX = spineWidth * 0.5 + pageWidth;
|
||||||
const bundleSpacing = calculateBundleSpacing(bundleCount, spineWidth, leftCount);
|
const bundleSpacing = calculateBundleSpacing(bundleCount, spineWidth, leftCount);
|
||||||
activeSpineHalf = spineWidth * 0.5;
|
activeSpineHalf = spineWidth * 0.5;
|
||||||
const lines = simulatePageLines(bundleCount, pageWidth, spineWidth, hingeX, bundleSpacing, leftCount);
|
const lines = simulatePageLines(bundleCount, pageWidth, pageSplineLength, spineWidth, foreEdgeX, bundleSpacing, leftCount);
|
||||||
lastLengthError = measureLineLengthError(lines, pageWidth);
|
lastLengthError = measureLineLengthError(lines, pageSplineLength);
|
||||||
lastSpacingError = measureStackSpacingError(lines, bundleSpacing);
|
lastSpacingError = measureStackSpacingError(lines, bundleSpacing);
|
||||||
lastBookModel = { coverDepth, pageWidth, pageDepth, bundleCount, spineWidth, hingeX, bundleSpacing, lines };
|
lastBookModel = { coverDepth, pageWidth, pageSplineLength, pageDepth, bundleCount, spineWidth, hingeX, foreEdgeX, bundleSpacing, lines };
|
||||||
|
|
||||||
addCoverAssembly(pageWidth, coverDepth, coverThickness, spineWidth);
|
addCoverAssembly(pageWidth, coverDepth, coverThickness, spineWidth);
|
||||||
addClothSpine(pageDepth, spineWidth);
|
addClothSpine(pageDepth, spineWidth);
|
||||||
@@ -244,7 +247,7 @@ function addCoverAssembly(pageWidth, depth, thickness, spineWidth) {
|
|||||||
function createCoverAssemblyGeometry(pageWidth, depth, thickness, spineWidth) {
|
function createCoverAssemblyGeometry(pageWidth, depth, thickness, spineWidth) {
|
||||||
const spineHalf = spineWidth * 0.5;
|
const spineHalf = spineWidth * 0.5;
|
||||||
const hingeX = spineHalf + HINGE_INSET;
|
const hingeX = spineHalf + HINGE_INSET;
|
||||||
const outerX = hingeX + pageWidth + COVER_OVERHANG;
|
const outerX = spineHalf + pageWidth + COVER_OVERHANG;
|
||||||
const outerTopY = BOOK_PROFILE.tableY + thickness;
|
const outerTopY = BOOK_PROFILE.tableY + thickness;
|
||||||
const connectionTopY = BOOK_PROFILE.raisedHingeY;
|
const connectionTopY = BOOK_PROFILE.raisedHingeY;
|
||||||
const spineTopY = BOOK_PROFILE.tableY + thickness;
|
const spineTopY = BOOK_PROFILE.tableY + thickness;
|
||||||
@@ -380,9 +383,13 @@ function calculateMaximumPageCount() {
|
|||||||
function isBundleCountReachable(bundleCount) {
|
function isBundleCountReachable(bundleCount) {
|
||||||
const spineWidth = calculateSpineWidth(bundleCount);
|
const spineWidth = calculateSpineWidth(bundleCount);
|
||||||
const bundleSpacing = calculateBundleSpacing(bundleCount, spineWidth, bundleCount);
|
const bundleSpacing = calculateBundleSpacing(bundleCount, spineWidth, bundleCount);
|
||||||
|
const foreEdgeX = spineWidth * 0.5 + PAGE_WIDTH;
|
||||||
|
const target = restingTarget(1, foreEdgeX, bundleCount - 1, bundleCount, bundleSpacing);
|
||||||
const anchor = spineCurvePoint(1, spineWidth);
|
const anchor = spineCurvePoint(1, spineWidth);
|
||||||
const topLineY = BOOK_PROFILE.coverThickness + BOOK_PROFILE.paperContactOffset + (bundleCount - 1) * bundleSpacing;
|
const chordLength = Math.hypot(target.x - anchor.x, target.y - anchor.y);
|
||||||
return topLineY - anchor.y <= MAX_SUPPORTED_STACK_RISE;
|
const solverSlack = PAGE_SPLINE_LENGTH - chordLength;
|
||||||
|
const minimumSlack = PAGE_SPLINE_LENGTH / (PAGE_LINE_SEGMENTS + 1);
|
||||||
|
return solverSlack >= minimumSlack;
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateBundleSpacing(bundleCount, spineWidth, leftCount) {
|
function calculateBundleSpacing(bundleCount, spineWidth, leftCount) {
|
||||||
@@ -408,10 +415,10 @@ function calculateLeftBundleCount(bundleCount) {
|
|||||||
return THREE.MathUtils.clamp(Math.round(bundleCount * readingProgress), 0, bundleCount);
|
return THREE.MathUtils.clamp(Math.round(bundleCount * readingProgress), 0, bundleCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function simulatePageLines(bundleCount, pageWidth, spineWidth, hingeX, bundleSpacing, leftCount) {
|
function simulatePageLines(bundleCount, pageWidth, pageSplineLength, spineWidth, foreEdgeX, bundleSpacing, leftCount) {
|
||||||
const lines = [];
|
const lines = [];
|
||||||
const segments = 24;
|
const segments = PAGE_LINE_SEGMENTS;
|
||||||
const stepLength = pageWidth / segments;
|
const stepLength = pageSplineLength / segments;
|
||||||
const entries = [];
|
const entries = [];
|
||||||
const spineArc = buildSpineArcSamples(spineWidth);
|
const spineArc = buildSpineArcSamples(spineWidth);
|
||||||
const rightCount = bundleCount - leftCount;
|
const rightCount = bundleCount - leftCount;
|
||||||
@@ -452,7 +459,7 @@ function simulatePageLines(bundleCount, pageWidth, spineWidth, hingeX, bundleSpa
|
|||||||
let lowerLine = null;
|
let lowerLine = null;
|
||||||
sideEntries.forEach((entry, rank) => {
|
sideEntries.forEach((entry, rank) => {
|
||||||
const anchor = spineCurvePoint(entry.t, spineWidth);
|
const anchor = spineCurvePoint(entry.t, spineWidth);
|
||||||
const target = restingTarget(side, pageWidth, hingeX, rank, sideEntries.length, bundleSpacing);
|
const target = restingTarget(side, foreEdgeX, rank, sideEntries.length, bundleSpacing);
|
||||||
const points = buildSupportSolvedLine(anchor, target, lowerLine, side, segments, stepLength, bundleCount, bundleSpacing);
|
const points = buildSupportSolvedLine(anchor, target, lowerLine, side, segments, stepLength, bundleCount, bundleSpacing);
|
||||||
const line = { index: entry.index, t: entry.t, side, anchor, points, endpoint: points[points.length - 1], isHairPage: entry.isHairPage === true };
|
const line = { index: entry.index, t: entry.t, side, anchor, points, endpoint: points[points.length - 1], isHairPage: entry.isHairPage === true };
|
||||||
lines.push(line);
|
lines.push(line);
|
||||||
@@ -540,10 +547,10 @@ function initialPageLine(anchor, target, segments) {
|
|||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
function restingTarget(side, pageWidth, hingeX, rank, sideCount, bundleSpacing) {
|
function restingTarget(side, foreEdgeX, rank, sideCount, bundleSpacing) {
|
||||||
const local = sideCount <= 1 ? 0 : rank / (sideCount - 1);
|
const local = sideCount <= 1 ? 0 : rank / (sideCount - 1);
|
||||||
const foreCurve = 0.11 * Math.sin(Math.PI * local);
|
const foreCurve = 0.11 * Math.sin(Math.PI * local);
|
||||||
const x = side * (hingeX + pageWidth - foreCurve);
|
const x = side * (foreEdgeX - foreCurve);
|
||||||
const y = BOOK_PROFILE.coverThickness + BOOK_PROFILE.paperContactOffset + rank * bundleSpacing + 0.002 * Math.sin(Math.PI * local);
|
const y = BOOK_PROFILE.coverThickness + BOOK_PROFILE.paperContactOffset + rank * bundleSpacing + 0.002 * Math.sin(Math.PI * local);
|
||||||
return { x, y };
|
return { x, y };
|
||||||
}
|
}
|
||||||
@@ -824,7 +831,7 @@ function coverTopYAtX(x) {
|
|||||||
const ax = Math.abs(x);
|
const ax = Math.abs(x);
|
||||||
const spineHalf = currentSpineHalf();
|
const spineHalf = currentSpineHalf();
|
||||||
const hingeX = spineHalf + HINGE_INSET;
|
const hingeX = spineHalf + HINGE_INSET;
|
||||||
const outerX = hingeX + PAGE_WIDTH + COVER_SUPPORT_OVERHANG;
|
const outerX = spineHalf + PAGE_WIDTH + COVER_SUPPORT_OVERHANG;
|
||||||
if (ax <= spineHalf) return BOOK_PROFILE.coverThickness;
|
if (ax <= spineHalf) return BOOK_PROFILE.coverThickness;
|
||||||
if (ax <= hingeX) {
|
if (ax <= hingeX) {
|
||||||
const t = (ax - spineHalf) / (hingeX - spineHalf);
|
const t = (ax - spineHalf) / (hingeX - spineHalf);
|
||||||
|
|||||||
@@ -74,6 +74,6 @@
|
|||||||
<button id="fast_forward" type="button">Fast Forward</button>
|
<button id="fast_forward" type="button">Fast Forward</button>
|
||||||
<output id="flip_count">0 / 10</output>
|
<output id="flip_count">0 / 10</output>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/js/webgl-book-shape-lab.js?v=hinge-relative-stacks-1"></script>
|
<script type="module" src="/js/webgl-book-shape-lab.js?v=spline-length-foreedge-1"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user