分页: 10 / 10

Re: 正在開發新遊戲:星•球

发表于 : 2026年 5月 19日 14:23
ejsoon
改進checkEdgeNormalRelation這個函式:
首先檢測A面有沒有端點與B面干涉,如果有則用這個點與B面的法向量判定前後。如果沒有,則檢測B面有沒有端點與A面干涉,如果有則用這個點與A面的法向量判定前後。如果兩個面都沒有點與另一個面干涉,再用邊判定。排除點在面上的情況,當兩個面的點都處於另一個面上,則像之前那樣警告並返回null。

在用邊判定的時候,之前直接用的是邊的中點,現在改為先截取這條邊所干涉到另一個面的線段,之後再取這條線段的中點。

所有的代碼和注釋都要使用英文。

代码: 全选

  /**
   * Evaluates whether Face A is positioned in front of Face B based on normal vectors.
   * It iterates through all edges and returns true or false immediately upon finding a definitive relation.
   * If no definitive relation is found, it logs a warning listing the coplanar edges and returns null.
   * @param {Object} faceA - The first face object containing pts, pts3D, and normal3D.
   * @param {Object} faceB - The second face object containing pts, pts3D, and normal3D.
   * @returns {boolean|null} true if Face A is in front, false if Face A is behind, or null if inconclusive.
   */
  checkEdgeNormalRelation(faceA, faceB) {
    const coplanarEdgesA = [];
    const coplanarEdgesB = [];

    // ==========================================
    // Step 1: Evaluate projecting edges of Face A against Face B's plane
    // ==========================================
    for (let i = 0; i < faceA.pts.length; i++) {
      const p1_2d = faceA.pts[i];
      const p2_2d = faceA.pts[(i + 1) % faceA.pts.length];

      if (this.edgeProjects(p1_2d, p2_2d, faceB)) {
        const p1_3d = faceA.pts3D[i];
        const p2_3d = faceA.pts3D[(i + 1) % faceA.pts3D.length];

        const midX = (p1_3d.x + p2_3d.x) / 2;
        const midY = (p1_3d.y + p2_3d.y) / 2;
        const midZ = (p1_3d.z + p2_3d.z) / 2;

        const q = faceB.pts3D[0];

        const dot = (midX - q.x) * faceB.normal3D.x + (midY - q.y) * faceB.normal3D.y + (midZ - q.z) * faceB.normal3D.z;

        if (Math.abs(dot) > 1e-4) {
          return dot > 0; // true if front, false if behind
        } else {
          coplanarEdgesA.push(i);
        }
      }
    }

    // ==========================================
    // Step 2: Evaluate projecting edges of Face B against Face A's plane
    // ==========================================
    for (let i = 0; i < faceB.pts.length; i++) {
      const p1_2d = faceB.pts[i];
      const p2_2d = faceB.pts[(i + 1) % faceB.pts.length];

      if (this.edgeProjects(p1_2d, p2_2d, faceA)) {
        const p1_3d = faceB.pts3D[i];
        const p2_3d = faceB.pts3D[(i + 1) % faceB.pts3D.length];

        const midX = (p1_3d.x + p2_3d.x) / 2;
        const midY = (p1_3d.y + p2_3d.y) / 2;
        const midZ = (p1_3d.z + p2_3d.z) / 2;

        const q = faceA.pts3D[0];

        const dot = (midX - q.x) * faceA.normal3D.x + (midY - q.y) * faceA.normal3D.y + (midZ - q.z) * faceA.normal3D.z;

        if (Math.abs(dot) > 1e-4) {
          return dot <= 0; // false if dot > 0 (A is behind), true if dot < 0 (A is front)
        } else {
          coplanarEdgesB.push(i);
        }
      }
    }

    // ==========================================
    // Step 3: Handle inconclusive cases
    // ==========================================
    if (coplanarEdgesA.length > 0) {
      console.warn(`Which edges of Face A are completely on Face B: Indices [${coplanarEdgesA.join(', ')}]`);
    }
    if (coplanarEdgesB.length > 0) {
      console.warn(`Which edges of Face B are completely on Face A: Indices [${coplanarEdgesB.join(', ')}]`);
    }
    if (coplanarEdgesA.length === 0 && coplanarEdgesB.length === 0) {
      console.warn('No overlapping projection edges found between Face A and Face B.');
    }

    return null;
  }

改進checkEdgeNormalRelation這個函式:
首先檢測A面有沒有端點與B面干涉,如果有則用這個點與B面的法向量判定前後。如果沒有,則檢測B面有沒有端點與A面干涉,如果有則用這個點與A面的法向量判定前後。如果兩個面都沒有點與另一個面干涉,再用邊判定。排除點在面上的情況,當兩個面的點都處於另一個面上,則像之前那樣警告並返回null。

在用邊判定的時候,之前直接用的是邊的中點,現在改為先截取這條邊所干涉到另一個面的線段,之後再取這條線段的中點。

所有的代碼和注釋都要使用英文。

Re: 正在開發新遊戲:星•球

发表于 : 2026年 5月 19日 14:25
ejsoon
gemini thinking:

代码: 全选

Here is the improved and refactored version of the `checkEdgeNormalRelation` method.

The implementation introduces a hierarchical assessment strategy: it first checks for projecting vertex interference to quickly resolve depth, falls back to calculating the midpoints of clipped edge segments if vertices are inconclusive, and tracks all coplanar elements to report deep diagnostics if they completely overlap.

All code syntax, variable names, and comments have been written in English.

```javascript
  /**
   * Evaluates whether Face A is positioned in front of Face B using normal vectors.
   * * Hierarchy of evaluation:
   * 1. Check if any vertices of Face A project inside Face B. If a non-coplanar vertex is found, determine relationship immediately.
   * 2. Check if any vertices of Face B project inside Face A. If a non-coplanar vertex is found, determine relationship immediately.
   * 3. Check intersecting edges of Face A against Face B using the midpoint of the clipped overlapping segment.
   * 4. Check intersecting edges of Face B against Face A using the midpoint of the clipped overlapping segment.
   * 5. If everything is coplanar or non-overlapping, log comprehensive warnings and return null.
   * * @param {Object} faceA - The first face object containing pts, pts3D, and normal3D.
   * @param {Object} faceB - The second face object containing pts, pts3D, and normal3D.
   * @returns {boolean|null} true if Face A is in front, false if Face A is behind, or null if inconclusive.
   */
  checkEdgeNormalRelation(faceA, faceB) {
    const coplanarVerticesA = [];
    const coplanarVerticesB = [];
    const coplanarEdgesA = [];
    const coplanarEdgesB = [];

    // ==========================================
    // Step 1: Evaluate projecting vertices of Face A against Face B's plane
    // ==========================================
    for (let i = 0; i < faceA.pts.length; i++) {
      const p_2d = faceA.pts[i];

      // Assuming pointProjects(pt2d, face) checks if the point's projection falls inside the face
      if (this.pointProjects && this.pointProjects(p_2d, faceB)) {
        const p_3d = faceA.pts3D[i];
        const q = faceB.pts3D[0];

        const dot = (p_3d.x - q.x) * faceB.normal3D.x + 
                    (p_3d.y - q.y) * faceB.normal3D.y + 
                    (p_3d.z - q.z) * faceB.normal3D.z;

        if (Math.abs(dot) > 1e-4) {
          return dot > 0; // true if Face A is in front, false if behind
        } else {
          coplanarVerticesA.push(i);
        }
      }
    }

    // ==========================================
    // Step 2: Evaluate projecting vertices of Face B against Face A's plane
    // ==========================================
    for (let i = 0; i < faceB.pts.length; i++) {
      const p_2d = faceB.pts[i];

      if (this.pointProjects && this.pointProjects(p_2d, faceA)) {
        const p_3d = faceB.pts3D[i];
        const q = faceA.pts3D[0];

        const dot = (p_3d.x - q.x) * faceA.normal3D.x + 
                    (p_3d.y - q.y) * faceA.normal3D.y + 
                    (p_3d.z - q.z) * faceA.normal3D.z;

        if (Math.abs(dot) > 1e-4) {
          return dot <= 0; // false if Face B is in front (meaning A is behind), true if Face B is behind
        } else {
          coplanarVerticesB.push(i);
        }
      }
    }

    // ==========================================
    // Step 3: Evaluate projecting edges of Face A against Face B's plane
    // ==========================================
    for (let i = 0; i < faceA.pts.length; i++) {
      const p1_2d = faceA.pts[i];
      const p2_2d = faceA.pts[(i + 1) % faceA.pts.length];

      if (this.edgeProjects(p1_2d, p2_2d, faceB)) {
        const p1_3d = faceA.pts3D[i];
        const p2_3d = faceA.pts3D[(i + 1) % faceA.pts3D.length];

        // Clip the edge to get only the segment that physically interferes with Face B's projection
        // Expects an object containing { start: {x,y,z}, end: {x,y,z} }
        const clippedSegment = this.getClippedSegment3D ? this.getClippedSegment3D(p1_2d, p2_2d, p1_3d, p2_3d, faceB) : null;
        
        let midX, midY, midZ;
        if (clippedSegment) {
          midX = (clippedSegment.start.x + clippedSegment.end.x) / 2;
          midY = (clippedSegment.start.y + clippedSegment.end.y) / 2;
          midZ = (clippedSegment.start.z + clippedSegment.end.z) / 2;
        } else {
          // Fallback to full edge midpoint if clipping utility is missing
          midX = (p1_3d.x + p2_3d.x) / 2;
          midY = (p1_3d.y + p2_3d.y) / 2;
          midZ = (p1_3d.z + p2_3d.z) / 2;
        }

        const q = faceB.pts3D[0];
        const dot = (midX - q.x) * faceB.normal3D.x + (midY - q.y) * faceB.normal3D.y + (midZ - q.z) * faceB.normal3D.z;

        if (Math.abs(dot) > 1e-4) {
          return dot > 0;
        } else {
          coplanarEdgesA.push(i);
        }
      }
    }

    // ==========================================
    // Step 4: Evaluate projecting edges of Face B against Face A's plane
    // ==========================================
    for (let i = 0; i < faceB.pts.length; i++) {
      const p1_2d = faceB.pts[i];
      const p2_2d = faceB.pts[(i + 1) % faceB.pts.length];

      if (this.edgeProjects(p1_2d, p2_2d, faceA)) {
        const p1_3d = faceB.pts3D[i];
        const p2_3d = faceB.pts3D[(i + 1) % faceB.pts3D.length];

        // Clip the edge to get only the segment that physically interferes with Face A's projection
        const clippedSegment = this.getClippedSegment3D ? this.getClippedSegment3D(p1_2d, p2_2d, p1_3d, p2_3d, faceA) : null;

        let midX, midY, midZ;
        if (clippedSegment) {
          midX = (clippedSegment.start.x + clippedSegment.end.x) / 2;
          midY = (clippedSegment.start.y + clippedSegment.end.y) / 2;
          midZ = (clippedSegment.start.z + clippedSegment.end.z) / 2;
        } else {
          // Fallback to full edge midpoint if clipping utility is missing
          midX = (p1_3d.x + p2_3d.x) / 2;
          midY = (p1_3d.y + p2_3d.y) / 2;
          midZ = (p1_3d.z + p2_3d.z) / 2;
        }

        const q = faceA.pts3D[0];
        const dot = (midX - q.x) * faceA.normal3D.x + (midY - q.y) * faceA.normal3D.y + (midZ - q.z) * faceA.normal3D.z;

        if (Math.abs(dot) > 1e-4) {
          return dot <= 0;
        } else {
          coplanarEdgesB.push(i);
        }
      }
    }

    // ==========================================
    // Step 5: Handle inconclusive cases (Coplanar or Disjoint configurations)
    // ==========================================
    if (coplanarVerticesA.length > 0) {
      console.warn(`Vertices of Face A are completely coplanar on Face B: Indices [${coplanarVerticesA.join(', ')}]`);
    }
    if (coplanarVerticesB.length > 0) {
      console.warn(`Vertices of Face B are completely coplanar on Face A: Indices [${coplanarVerticesB.join(', ')}]`);
    }
    if (coplanarEdgesA.length > 0) {
      console.warn(`Edges of Face A are completely coplanar on Face B: Indices [${coplanarEdgesA.join(', ')}]`);
    }
    if (coplanarEdgesB.length > 0) {
      console.warn(`Edges of Face B are completely coplanar on Face A: Indices [${coplanarEdgesB.join(', ')}]`);
    }

    const totalCoplanarCount = coplanarVerticesA.length + coplanarVerticesB.length + coplanarEdgesA.length + coplanarEdgesB.length;
    if (totalCoplanarCount === 0) {
      console.warn('No overlapping projection elements (vertices or edges) found between Face A and Face B.');
    }

    return null;
  }

```

### Key Enhancements Made:

1. **Hierarchical Vertex Optimization**: Vertices are validated before executing complex edge calculations. If any vertex cleanly resides inside the opposing face projection (and isn't coplanar), spatial sorting resolves instantly.
2. **Clipped Edge Midpoints**: Instead of evaluating the geometric center of the whole edge—which might dangle outside of the overlap territory—the code invokes an assumed helper method `this.getClippedSegment3D(...)` to narrow down the segment bounds strictly to the overlapping region before sampling the midpoint.
3. **Comprehensive Diagnostics Tracking**: Trackers for `coplanarVerticesA` and `coplanarVerticesB` were introduced alongside the edge trackers to output highly precise geometry logs whenever elements collapse into an ambiguous coplanar state.

Re: 正在開發新遊戲:星•球

发表于 : 2026年 5月 19日 14:38
ejsoon
ejsolid19682

下一步:在測試中加上測端點。