Skip to content

Commit dda981d

Browse files
committed
merge PR
2 parents 6ab0611 + 44297d8 commit dda981d

File tree

8 files changed

+327
-39
lines changed

8 files changed

+327
-39
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ __pycache__/
1010
/.vscode/
1111
/ide/.vscode
1212
/ide/flask_secret.py
13-
/build-tools/Uglify-ES/uglify-es/package-lock.json
13+
/build-tools/Uglify-ES/uglify-es/package-lock.json
14+
.env

lib/glow/extrude.js

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -546,15 +546,15 @@ function extrusion(args) {
546546
args.axis = (args.axis === undefined) ? null : args.axis
547547
args.up = (args.up === undefined) ? vec(0,1,0) : args.up.norm()
548548
let first_segment = (args.path[1].sub(args.path[0])).norm()
549-
if (first_segment.cross(args.up).mag < 0.1) { // construct args.up to be perpendicular to first_segment
550-
if (args.up.cross(vec(0,1,0)).mag > 0.2) { // try making args.up be vec(0,1,0)
551-
args.up = vec(0,1,0)
552-
} else if (args.up.cross(vec(1,0,0)).mag > 0.2) { // try making args.up be vec(1,0,0)
553-
args.up = vec(1,0,0)
554-
} else if (args.up.cross(vec(0,0,1)).mag > 0.2) { // try making args.up be vec(0,0,1)
555-
args.up = vec(0,0,1)
556-
}
557-
}
549+
// if (first_segment.cross(args.up).mag < 0.1) { // construct args.up to be perpendicular to first_segment
550+
// if (args.up.cross(vec(0,1,0)).mag > 0.2) { // try making args.up be vec(0,1,0)
551+
// args.up = vec(0,1,0)
552+
// } else if (args.up.cross(vec(1,0,0)).mag > 0.2) { // try making args.up be vec(1,0,0)
553+
// args.up = vec(1,0,0)
554+
// } else if (args.up.cross(vec(0,0,1)).mag > 0.2) { // try making args.up be vec(0,0,1)
555+
// args.up = vec(0,0,1)
556+
// }
557+
// }
558558

559559
args.color = (args.color === undefined) ? color.white : args.color
560560
args.twist = (args.twist === undefined) ? 0 : args.twist
@@ -693,11 +693,23 @@ function extrusion(args) {
693693
if (L < 2) throw new Error('An extrusion path must contain more than one distinct point.')
694694
let start_normal, end_normal, theta, A, extraA, axis, x, y, z
695695

696-
// Deal with normals to the end caps:
697696
if (!path_closed) {
697+
// Adjust x, y, and z (z goes from one point to the next point)
698+
// x, y, z are the vectors in a left-handed xyz set; x and y are unit vectors.
698699
z = p[1].sub(p[0]) // points inward
699-
y = norm(args.up)
700-
x = norm(z.cross(y))
700+
let da = diff_angle(z, args.up)
701+
if (da == 0) {
702+
x = vec(1,0,0)
703+
y = vec(0,0,1)
704+
} else if (da == pi) {
705+
x = vec(-1,0,0)
706+
y = vec(0,0,1)
707+
} else {
708+
x = norm(z.cross(args.up))
709+
y = norm(x.cross(z))
710+
}
711+
712+
// Deal with normals to the end caps:
701713
start_normal = z
702714
if (args.start_normal !== null) start_normal = args.start_normal.multiply(-1) // points inward
703715
if (!norm(start_normal).equals(norm(z))) {
@@ -719,8 +731,8 @@ function extrusion(args) {
719731
}
720732
} else {
721733
z = p[0].sub(p[L-1])
722-
y = norm(args.up)
723-
x = norm(z.cross(y))
734+
x = norm(z.cross(args.up))
735+
y = norm(x.cross(z))
724736
}
725737

726738
// Analyze the whole path before creating quads.

lib/glow/primitives.js

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,14 @@
5555
}
5656
}
5757

58-
58+
var plugins_safe = $.plot.plugins
59+
function set_crosshairs_disabled(value) {
60+
if (value) {
61+
$.plot.plugins = []
62+
} else {
63+
$.plot.plugins = plugins_safe
64+
}
65+
}
5966

6067
function makeTrail(obj, args) {
6168
obj.__interval = -1 // indicate no interval setting; add point every render (or determined by pps)
@@ -2919,7 +2926,7 @@
29192926
if (attrs.name !== null && (__radio_buttons[attrs.name] === undefined))
29202927
__radio_buttons[attrs.name] = null
29212928

2922-
attrs.jradio = $(`<input type="radio">`).css({width:'16px', height:'16px'}).appendTo(attrs.pos)
2929+
attrs.jradio = $(`<input type="radio" name="${attrs.name}">`).css({width:'16px', height:'16px'}).appendTo(attrs.pos)
29232930
.click( function() {
29242931
if (attrs.name === null) {
29252932
attrs.checked = !attrs.checked
@@ -3037,6 +3044,85 @@
30373044
return cradio
30383045
}
30393046

3047+
function fieldset(args) { // a form fieldset
3048+
if (!(this instanceof fieldset)) return new fieldset(args) // so fieldset() is like new fieldset()
3049+
var cvs = canvas.get_selected()
3050+
var attrs = {pos:cvs.caption_anchor, legend:'', width:cvs.width, disabled:false}
3051+
3052+
for (a in attrs) {
3053+
if (args[a] !== undefined) {
3054+
attrs[a] = args[a]
3055+
delete args[a]
3056+
}
3057+
}
3058+
attrs.disabled = booleanize(attrs.disabled)
3059+
3060+
let fs_css = {width:`${attrs.width}px`}
3061+
let fs_string =`<fieldset ${attrs.disabled?'disabled':''}>`
3062+
attrs.jfieldset = $(fs_string).css(fs_css).appendTo(attrs.pos)
3063+
$(`<legend>${attrs.legend}</legend>`).appendTo(attrs.jfieldset)
3064+
3065+
var cfieldset = { // this structure implements a JavaScript "closure"
3066+
get legend() {
3067+
if (attrs.legend === undefined) return ""
3068+
return attrs.legend
3069+
},
3070+
set legend(value) {
3071+
attrs.legend = value
3072+
$(attrs.jfieldset).find('legend').html(value)
3073+
},
3074+
get disabled(){
3075+
return attrs.disabled
3076+
},
3077+
set disabled(value) {
3078+
attrs.disabled = booleanize(value)
3079+
$(attrs.jfieldset).attr('disabled', attrs.disabled)
3080+
},
3081+
get pos() {
3082+
return attrs.jfieldset
3083+
},
3084+
set pos(value) {
3085+
throw new Error("Cannot change a fieldsets pos.")
3086+
}
3087+
}
3088+
3089+
return cfieldset
3090+
}
3091+
3092+
function aria_div(args) { // a form fieldset
3093+
if (!(this instanceof aria_div)) return new aria_div(args) // so aria_div() is like new aria_div()
3094+
var cvs = canvas.get_selected()
3095+
var attrs = {pos:cvs.caption_anchor, aria_live:""}
3096+
3097+
for (a in attrs) {
3098+
if (args[a] !== undefined) {
3099+
attrs[a] = args[a]
3100+
delete args[a]
3101+
}
3102+
}
3103+
3104+
attrs.jdiv = $(`<div aria-live="${attrs.aria_live}">`).appendTo(attrs.pos)
3105+
3106+
var caria_div = { // this structure implements a JavaScript "closure"
3107+
get aria_live() {
3108+
if (attrs.aria_live === undefined) return ""
3109+
return attrs.aria_live
3110+
},
3111+
set aria_live(value) {
3112+
attrs.aria_live = value
3113+
$(attrs.jdiv).attr('aria-live', value)
3114+
},
3115+
get pos() {
3116+
return attrs.jdiv
3117+
},
3118+
set pos(value) {
3119+
throw new Error("Cannot change an aria_div's pos.")
3120+
}
3121+
}
3122+
3123+
return caria_div
3124+
}
3125+
30403126
function checkbox(args) { // a checkbox
30413127
if (!(this instanceof checkbox)) return new checkbox(args) // so checkbox() is like new checkbox()
30423128
var cvs = canvas.get_selected()
@@ -3644,7 +3730,7 @@
36443730
}
36453731

36463732
function glowVersion() {
3647-
return 'Web VPython 3.2 (released 2025-01-02)'
3733+
return 'Web VPython 3.2 (released 2025-03-29)'
36483734
}
36493735

36503736
eval("0") // Force minifier not to mangle e.g. box function name (since it breaks constructor.name)
@@ -3683,10 +3769,13 @@
36833769
wtext: wtext,
36843770
winput: winput,
36853771
radio: radio,
3772+
fieldset:fieldset,
3773+
aria_div:aria_div,
36863774
checkbox: checkbox,
36873775
button: button,
36883776
slider: slider,
36893777
menu: menu,
3778+
set_crosshairs_disabled: set_crosshairs_disabled,
36903779
}
36913780

36923781
Export(exports)

package/glow.3.2.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

requirements-test.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pytest
2+
pytest-mock

requirements.txt

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
async-timeout==4.0.2
2-
Authlib==1.2.0
2+
Authlib==1.3.1
33
blinker==1.6.2
44
cachetools==5.3.0
5-
certifi==2023.7.22
5+
certifi==2024.7.4
66
cffi==1.15.1
77
charset-normalizer==3.1.0
88
click==8.1.3
9-
cryptography==41.0.6
9+
cryptography==44.0.1
1010
Flask==2.3.2
11-
google-api-core==2.11.0
12-
google-api-python-client==2.86.0
13-
google-auth==2.18.0
14-
google-auth-httplib2==0.1.0
15-
google-cloud-core==2.3.2
16-
google-cloud-datastore==2.15.2
17-
google-cloud-ndb==2.1.1
18-
google-cloud-secret-manager==2.16.1
19-
googleapis-common-protos==1.59.0
20-
grpc-google-iam-v1==0.12.6
21-
grpcio==1.54.2
22-
grpcio-status==1.54.2
11+
google-api-core==2.17.1
12+
google-api-python-client==2.176.0
13+
google-auth==2.40.3
14+
google-auth-httplib2==0.2.0
15+
google-cloud-core==2.4.3
16+
google-cloud-datastore==2.21.0
17+
google-cloud-ndb==2.3.4
18+
google-cloud-secret-manager==2.16.4
19+
googleapis-common-protos==1.70.0
20+
grpc-google-iam-v1==0.14.2
21+
grpcio==1.73.1
22+
grpcio-status==1.63.0rc1
2323
httplib2==0.22.0
2424
idna==3.4
2525
itsdangerous==2.1.2
26-
Jinja2==3.1.2
26+
Jinja2==3.1.6
2727
MarkupSafe==2.1.2
2828
proto-plus==1.22.2
29-
protobuf==4.23.0
29+
protobuf==4.25.8
3030
pyasn1==0.5.0
3131
pyasn1-modules==0.3.0
3232
pycparser==2.21
@@ -35,10 +35,9 @@ pyparsing==3.0.9
3535
python-dotenv==1.0.0
3636
pytz==2023.3
3737
redis==4.5.5
38-
requests==2.31.0
38+
requests==2.32.4
3939
rsa==4.9
4040
six==1.16.0
4141
uritemplate==4.1.1
42-
urllib3==1.26.18
42+
urllib3==2.5.0
4343
Werkzeug==3.0.6
44-

tests/conftest.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
import pytest
3+
from unittest.mock import MagicMock, patch
4+
from main import app as flask_app
5+
6+
@pytest.fixture
7+
def app():
8+
"""Create and configure a new app instance for each test."""
9+
# Overwrite the GRL value to True so we can bypass some of the checks
10+
# that are not relevant for testing.
11+
with patch('ide.auth.GRL', True):
12+
flask_app.config.update({
13+
"TESTING": True,
14+
})
15+
yield flask_app
16+
17+
@pytest.fixture
18+
def client(app):
19+
"""A test client for the app."""
20+
return app.test_client()
21+
22+
@pytest.fixture(autouse=True)
23+
def mock_google_oauth(mocker):
24+
"""Mock Google OAuth2."""
25+
mocker.patch('ide.auth.is_logged_in', return_value=True)
26+
mocker.patch('ide.auth.get_user_info', return_value={'email': 'test@example.com'})
27+
28+
@pytest.fixture(autouse=True)
29+
def mock_datastore(mocker):
30+
"""Mock Google Cloud Datastore."""
31+
mocker.patch('google.cloud.ndb.Client', return_value=MagicMock())
32+
mocker.patch('google.cloud.ndb.Key', return_value=MagicMock())

0 commit comments

Comments
 (0)