#1901·penrose

nonDegenerateAngle incorrect behaviour for "range"

Author: AlexKubiesaCreated Jun 10, 2025Updated Jun 10, 2025

Describe the bug When I use nonDegenerateAngle with a small range value of 5, angles are forced to be much larger than 5°, so the range cutoff is not respected. I am using Bloom but the issue seems to be in penrose/core, so it affects Penrose as well.

Looking at the implementation

const c0 = shapeCenter(s0);
const c1 = shapeCenter(s1);
const c2 = shapeCenter(s2);

const l1 = ops.vsub(c0, c1);
const l2 = ops.vsub(c2, c1);
const cosine = absVal(ops.vdot(ops.vnormalize(l1), ops.vnormalize(l2)));
// angles that are more than `range` deg from 0 or 180 do not need to be pushed
return ifCond(
  lt(cosine, range * (Math.PI / 180)),
  0,
  mul(strength, cosine),
);

it seems that a cosine is being directly compared with an angle in lt(cosine, range * (Math.PI / 180)). I guess the correct thing to do would be to take the cosine of range * (Math.PI / 180) and compare against that instead.

To Reproduce

Domain:

type Angle

Substance:

Angle A

Style:

canvas {
  width = 500
  height = 500
}

forall Angle a {
  a.pos = (?, ?)
  
  a.line1 = Line {
    start : a.pos
    end : (?, ?)
    strokeColor : #000000
    strokeWidth : 1
    style : "solid"
  }

  a.line2 = Line {
    start : a.pos
    end : (?, ?)
    strokeColor : #000000
    strokeWidth : 1
    style : "solid"
  }

  a.p1 = Circle {
    r : 5
    fillColor : #000000
    center : a.line1.end
  }

  a.p2 = Circle {
    r : 5
    fillColor : #000000
    center : a.pos
  }

  a.p3 = Circle {
    r : 5
    fillColor : #000000
    center: a.line2.end
  }

  encourage nonDegenerateAngle(a.p1, a.p2, a.p3, 20, 1)
}

global {
    shape background = Rectangle {
        center: (0, 0)
        width: canvas.width
        height: canvas.height
        fillColor: #F6F4F2
    } 
}

Steps to reproduce the behavior:

  1. Put the Domain, Substance and Style above into the Penrose editor and compile.
  2. Resample a few times and see that the angle is never close to 1°. You can also drag the slider and see that the optimizer pushes it to be larger even though it's greater than 1°.
  3. Comment out the "encourage nonDegenerateAngle" line and repeat step 2. The behaviour is different: smaller angles are seen, and the optimizer barely affects the angle at all.

Expected behavior The range parameter should behave as documented. Setting range to 1 should not affect angles that are already between 1° and 179°. Setting range to 5 should not affect angles between 5° and 175°.

Screenshots Not included. Should be easy to reproduce.

Desktop (please complete the following information if applicable):

  • OS: Linux (Ubuntu)
  • Browser: Chromium
  • Version: 135.0.7049.114 (Official Build) snap (64-bit)

Additional context N/A