Skip to content

Commit

Permalink
Add lengthScale option
Browse files Browse the repository at this point in the history
  • Loading branch information
ricktu288 committed Jul 22, 2024
1 parent 68ed3da commit 4061d91
Show file tree
Hide file tree
Showing 40 changed files with 322 additions and 238 deletions.
15 changes: 11 additions & 4 deletions simulator/js/CanvasRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* A class to render geometric figures from geometry.js on a canvas, and to handle the transformation and background image of the canvas.
*/
class CanvasRenderer {
constructor(ctx, origin, scale, backgroundImage) {
constructor(ctx, origin, scale, lengthScale, backgroundImage) {

/** @property {Object} ctx - The context of the canvas. */
this.ctx = ctx;
Expand All @@ -13,6 +13,9 @@ class CanvasRenderer {
/** @property {number} scale - The scale factor (the viewport physical pixel per internal length unit) of the scene. */
this.scale = scale;

/** @property {number} lengthScale - The scale factor of the length units of the scene. */
this.lengthScale = lengthScale;

/** @property {Object} canvas - The canvas of the scene. */
this.canvas = ctx.canvas;

Expand All @@ -22,15 +25,15 @@ class CanvasRenderer {
/** @property {boolean} isSVG - Whether the canvas is being exported to SVG. */
this.isSVG = ctx.constructor === C2S;
}

/**
* Draw a point.
* @param {Point} p
* @param {String} [color='black']
*/
drawPoint(p, color = 'black') {
this.ctx.fillStyle = color;
this.ctx.fillRect(p.x - 2.5, p.y - 2.5, 5, 5);
this.ctx.fillRect(p.x - 2.5 * this.lengthScale, p.y - 2.5 * this.lengthScale, 5 * this.lengthScale, 5 * this.lengthScale);
}

/**
Expand All @@ -40,6 +43,7 @@ class CanvasRenderer {
*/
drawLine(l, color = 'black') {
this.ctx.strokeStyle = color;
this.ctx.lineWidth = 1 * this.lengthScale;
this.ctx.beginPath();
let ang1 = Math.atan2((l.p2.x - l.p1.x), (l.p2.y - l.p1.y));
let cvsLimit = (Math.abs(l.p1.x + this.origin.x) + Math.abs(l.p1.y + this.origin.y) + this.canvas.height + this.canvas.width) / Math.min(1, this.scale);
Expand All @@ -55,8 +59,9 @@ class CanvasRenderer {
*/
drawRay(r, color = 'black') {
this.ctx.strokeStyle = color;
this.ctx.lineWidth = 1 * this.lengthScale;
let ang1, cvsLimit;
if (Math.abs(r.p2.x - r.p1.x) > 1e-5 || Math.abs(r.p2.y - r.p1.y) > 1e-5) {
if (Math.abs(r.p2.x - r.p1.x) > 1e-5 * this.lengthScale || Math.abs(r.p2.y - r.p1.y) > 1e-5 * this.lengthScale) {
this.ctx.beginPath();
ang1 = Math.atan2((r.p2.x - r.p1.x), (r.p2.y - r.p1.y));
cvsLimit = (Math.abs(r.p1.x + this.origin.x) + Math.abs(r.p1.y + this.origin.y) + this.canvas.height + this.canvas.width) / Math.min(1, this.scale);
Expand All @@ -73,6 +78,7 @@ class CanvasRenderer {
*/
drawSegment(s, color = 'black') {
this.ctx.strokeStyle = color;
this.ctx.lineWidth = 1 * this.lengthScale;
this.ctx.beginPath();
this.ctx.moveTo(s.p1.x, s.p1.y);
this.ctx.lineTo(s.p2.x, s.p2.y);
Expand All @@ -86,6 +92,7 @@ class CanvasRenderer {
*/
drawCircle(c, color = 'black') {
this.ctx.strokeStyle = color;
this.ctx.lineWidth = 1 * this.lengthScale;
this.ctx.beginPath();
if (typeof c.r === 'object') {
let dx = c.r.p1.x - c.r.p2.x;
Expand Down
2 changes: 1 addition & 1 deletion simulator/js/Mouse.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class Mouse {
}
}

if (snapContext && x * x + y * y > Mouse.SNAP_TO_DIRECTION_LOCK_LIMIT * Mouse.SNAP_TO_DIRECTION_LOCK_LIMIT) {
if (snapContext && x * x + y * y > Mouse.SNAP_TO_DIRECTION_LOCK_LIMIT * Mouse.SNAP_TO_DIRECTION_LOCK_LIMIT / this.scene.scale / this.scene.scale) {
// lock the snap
snapContext.locked = true;
snapContext.i0 = i0;
Expand Down
12 changes: 7 additions & 5 deletions simulator/js/Scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const DATA_VERSION = 5;
* @property {boolean} lockObjs - The "Lock Objects" option indicating if the objects are locked.
* @property {number} gridSize - The size of the grid.
* @property {Circle|null} observer - The observer of the scene, null if not set.
* @property {number} lengthScale - The length scale used in line width, default font size, etc in the scene.
* @property {Point} origin - The origin of the scene in the viewport.
* @property {number} scale - The scale factor (the viewport CSS pixel per internal length unit) of the scene.
* @property {number} width - The width (in CSS pixels) of the viewport.
Expand All @@ -39,19 +40,20 @@ class Scene {
lockObjs: false,
gridSize: 20,
observer: null,
lengthScale: 1,
origin: { x: 0, y: 0 },
scale: 1,
width: 1500,
height: 900,
simulateColors: false,
symbolicBodyMerging: false
};

constructor() {
this.backgroundImage = null;
this.error = null;
this.warning = null;
this.fromJSON(JSON.stringify({version: DATA_VERSION}), () => {});
this.fromJSON(JSON.stringify({ version: DATA_VERSION }), () => { });
}

/**
Expand Down Expand Up @@ -135,7 +137,7 @@ class Scene {
}
this[key] = jsonData[key];
}

// Rescale the scene to fit the current viewport.
let rescaleFactor = 1;

Expand Down Expand Up @@ -179,13 +181,13 @@ class Scene {
* @returns {string} The JSON string representing the scene.
*/
toJSON() {
let jsonData = {version: DATA_VERSION};
let jsonData = { version: DATA_VERSION };

// Put the name of the scene first.
if (this.name) {
jsonData.name = this.name;
}

// And then the module definitions.
if (Object.keys(this.modules).length > 0) {
jsonData.modules = this.modules;
Expand Down
11 changes: 6 additions & 5 deletions simulator/js/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ function canvas_onmousemove(e) {
mousePos = mousePos2;


document.getElementById('mouseCoordinates').innerHTML = getMsg('mouse_coordinates') + "(" + Math.round(mousePos.x) + "," + Math.round(mousePos.y) + ")";
const mousePosDigits = Math.max(Math.round(Math.log10(scene.scale)), 0);
document.getElementById('mouseCoordinates').innerHTML = getMsg('mouse_coordinates') + "(" + mousePos.x.toFixed(mousePosDigits) + ", " + mousePos.y.toFixed(mousePosDigits) + ")";

if (isConstructing) {
// highlight object being constructed
Expand Down Expand Up @@ -473,14 +474,14 @@ function canvas_onmousewheel(e) {
// cross-browser wheel delta
var e = window.event || e; // old IE support
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
var d = scene.scale;
var d = scene.scale * scene.lengthScale;
if (delta < 0) {
d = scene.scale - 0.25;
d = scene.scale * scene.lengthScale - 0.25;
} else if (delta > 0) {
d = scene.scale + 0.25;
d = scene.scale * scene.lengthScale + 0.25;
}
d = Math.max(0.25, Math.min(5.00, d)) * 100;
setScaleWithCenter(d / 100, (e.pageX - e.target.offsetLeft) / scene.scale, (e.pageY - e.target.offsetTop) / scene.scale);
setScaleWithCenter(d / scene.lengthScale / 100, (e.pageX - e.target.offsetLeft) / scene.scale, (e.pageY - e.target.offsetTop) / scene.scale);
JSONOutput();
//window.toolBarViewModel.zoom.value(d);
canvas_onmousemove(e);
Expand Down
14 changes: 7 additions & 7 deletions simulator/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ window.onload = function (e) {
// Update scale based on previous scale and scaling factor
let newScale = lastScale * scaleFactor;

newScale = Math.max(0.25, Math.min(5.00, newScale));
newScale = Math.max(0.25/scene.lengthScale, Math.min(5.00/scene.lengthScale, newScale));

// Calculate the mid point between the two touches
const x = (e.touches[0].pageX + e.touches[1].pageX) / 2;
Expand Down Expand Up @@ -801,8 +801,8 @@ function initParameters() {
document.getElementById("rayDensity").value = scene.rayModeDensity;
document.getElementById("rayDensity_more").value = scene.rayModeDensity;
document.getElementById("rayDensity_mobile").value = scene.rayModeDensity;
document.getElementById("zoom").innerText = Math.round(scene.scale * 100) + '%';
document.getElementById("zoom_mobile").innerText = Math.round(scene.scale * 100) + '%';
document.getElementById("zoom").innerText = Math.round(scene.scale * scene.lengthScale * 100) + '%';
document.getElementById("zoom_mobile").innerText = Math.round(scene.scale * scene.lengthScale * 100) + '%';
toolbtn_clicked('');
modebtn_clicked('rays');
scene.backgroundImage = null;
Expand Down Expand Up @@ -1140,8 +1140,8 @@ function JSONInput() {
document.getElementById('gridSize').value = scene.gridSize;
document.getElementById('gridSize_mobile').value = scene.gridSize;

document.getElementById("zoom").innerText = Math.round(scene.scale * 100) + '%';
document.getElementById("zoom_mobile").innerText = Math.round(scene.scale * 100) + '%';
document.getElementById("zoom").innerText = Math.round(scene.scale * scene.lengthScale * 100) + '%';
document.getElementById("zoom_mobile").innerText = Math.round(scene.scale * scene.lengthScale * 100) + '%';
document.getElementById('simulateColors').checked = scene.simulateColors;
document.getElementById('simulateColors_mobile').checked = scene.simulateColors;
modebtn_clicked(scene.mode);
Expand Down Expand Up @@ -1205,8 +1205,8 @@ function setScaleWithCenter(value, centerX, centerY) {
scene.origin.x -= centerX * scaleChange;
scene.origin.y -= centerY * scaleChange;
scene.scale = value;
document.getElementById("zoom").innerText = Math.round(scene.scale * 100) + '%';
document.getElementById("zoom_mobile").innerText = Math.round(scene.scale * 100) + '%';
document.getElementById("zoom").innerText = Math.round(scene.scale * scene.lengthScale * 100) + '%';
document.getElementById("zoom_mobile").innerText = Math.round(scene.scale * scene.lengthScale * 100) + '%';
draw();
}

Expand Down
3 changes: 2 additions & 1 deletion simulator/js/objs/BaseGlass.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class BaseGlass extends BaseSceneObj {
*/
fillGlass(canvasRenderer, isAboveLight, isHovered) {
const ctx = canvasRenderer.ctx;
const ls = canvasRenderer.lengthScale;

const n = this.refIndex;

Expand Down Expand Up @@ -98,7 +99,7 @@ class BaseGlass extends BaseSceneObj {
// canvas2svg does not support globalCompositeOperation. Use the old appearance.
ctx.globalAlpha = 1;
ctx.strokeStyle = isHovered ? 'cyan' : ('rgb(70,70,70)');
ctx.lineWidth = 1;
ctx.lineWidth = 1 * ls;
ctx.stroke();
}
}
Expand Down
2 changes: 1 addition & 1 deletion simulator/js/objs/BaseGrinGlass.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class BaseGrinGlass extends BaseGlass {
});

if (objBar.showAdvanced(!this.arePropertiesDefault(['stepSize']))) {
objBar.createNumber(getMsg('stepSize'), 0.1, 1, 0.1, this.stepSize, function (obj, value) {
objBar.createNumber(getMsg('stepSize'), 0.1 * this.scene.lengthScale, 1 * this.scene.lengthScale, 0.1 * this.scene.lengthScale, this.stepSize, function (obj, value) {
obj.stepSize = parseFloat(value);
}, getMsg('stepSize_note_popover'));
}
Expand Down
2 changes: 1 addition & 1 deletion simulator/js/objs/CircleObjMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ const CircleObjMixin = Base => class extends Base {
const rp_exist = [];
const rp_lensq = [];
for (let i = 1; i <= 2; i++) {
rp_exist[i] = geometry.intersectionIsOnRay(rp_temp[i], ray) && geometry.distanceSquared(rp_temp[i], ray.p1) > minShotLength_squared;
rp_exist[i] = geometry.intersectionIsOnRay(rp_temp[i], ray) && geometry.distanceSquared(rp_temp[i], ray.p1) > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale;
rp_lensq[i] = geometry.distanceSquared(ray.p1, rp_temp[i]);
}

Expand Down
13 changes: 7 additions & 6 deletions simulator/js/objs/blocker/Aperture.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ objTypes['Aperture'] = class extends BaseFilter {
populateObjBar(objBar) {
var originalDiameter = geometry.distance(this.p3, this.p4);

objBar.createNumber(getMsg('diameter'), 0, 100, 1, originalDiameter, function (obj, value) {
objBar.createNumber(getMsg('diameter'), 0, 100 * this.scene.lengthScale, 1 * this.scene.lengthScale, originalDiameter, function (obj, value) {
var t = 0.5 * (1 - value / geometry.distance(obj.p1, obj.p2));
obj.p3 = geometry.point(obj.p1.x * (1 - t) + obj.p2.x * t, obj.p1.y * (1 - t) + obj.p2.y * t);
obj.p4 = geometry.point(obj.p1.x * t + obj.p2.x * (1 - t), obj.p1.y * t + obj.p2.y * (1 - t));
Expand All @@ -38,15 +38,16 @@ objTypes['Aperture'] = class extends BaseFilter {

draw(canvasRenderer, isAboveLight, isHovered) {
const ctx = canvasRenderer.ctx;
const ls = canvasRenderer.lengthScale;

if (this.p1.x == this.p2.x && this.p1.y == this.p2.y) {
ctx.fillStyle = 'rgb(128,128,128)';
ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3);
ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls);
return;
}

ctx.strokeStyle = isHovered ? 'cyan' : ((scene.simulateColors && this.wavelength && this.filter) ? wavelengthToColor(this.wavelength || GREEN_WAVELENGTH, 1) : 'rgb(70,35,10)');
ctx.lineWidth = 3;
ctx.lineWidth = 3 * ls;
ctx.lineCap = 'butt';
ctx.beginPath();
ctx.moveTo(this.p1.x, this.p1.y);
Expand All @@ -56,11 +57,11 @@ objTypes['Aperture'] = class extends BaseFilter {
ctx.moveTo(this.p2.x, this.p2.y);
ctx.lineTo(this.p4.x, this.p4.y);
ctx.stroke();
ctx.lineWidth = 1;
ctx.lineWidth = 1 * ls;
if (isHovered) {
ctx.fillStyle = 'magenta';
ctx.fillRect(this.p3.x - 1.5, this.p3.y - 1.5, 3, 3);
ctx.fillRect(this.p4.x - 1.5, this.p4.y - 1.5, 3, 3);
ctx.fillRect(this.p3.x - 1.5 * ls, this.p3.y - 1.5 * ls, 3 * ls, 3 * ls);
ctx.fillRect(this.p4.x - 1.5 * ls, this.p4.y - 1.5 * ls, 3 * ls, 3 * ls);
}
}

Expand Down
7 changes: 4 additions & 3 deletions simulator/js/objs/blocker/Blocker.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,22 @@ objTypes['Blocker'] = class extends LineObjMixin(BaseFilter) {

draw(canvasRenderer, isAboveLight, isHovered) {
const ctx = canvasRenderer.ctx;
const ls = canvasRenderer.lengthScale;

if (this.p1.x == this.p2.x && this.p1.y == this.p2.y) {
ctx.fillStyle = 'rgb(128,128,128)';
ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3);
ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls);
return;
}

ctx.strokeStyle = isHovered ? 'cyan' : ((scene.simulateColors && this.wavelength && this.filter) ? wavelengthToColor(this.wavelength || GREEN_WAVELENGTH, 1) : 'rgb(70,35,10)');
ctx.lineWidth = 3;
ctx.lineWidth = 3 * ls;
ctx.lineCap = 'butt';
ctx.beginPath();
ctx.moveTo(this.p1.x, this.p1.y);
ctx.lineTo(this.p2.x, this.p2.y);
ctx.stroke();
ctx.lineWidth = 1;
ctx.lineWidth = 1 * ls;
}

checkRayIntersects(ray) {
Expand Down
10 changes: 6 additions & 4 deletions simulator/js/objs/blocker/CircleBlocker.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,22 @@ objTypes['CircleBlocker'] = class extends CircleObjMixin(BaseFilter) {

draw(canvasRenderer, isAboveLight, isHovered) {
const ctx = canvasRenderer.ctx;
const ls = canvasRenderer.lengthScale;

ctx.beginPath();
ctx.arc(this.p1.x, this.p1.y, geometry.segmentLength(this), 0, Math.PI * 2);
ctx.lineWidth = 3;
ctx.lineWidth = 3 * ls;
ctx.strokeStyle = isHovered ? 'cyan' : ((scene.simulateColors && this.wavelength && this.filter) ? wavelengthToColor(this.wavelength || GREEN_WAVELENGTH, 1) : 'rgb(70,35,10)');
//ctx.fillStyle="indigo";

ctx.stroke();
ctx.fillStyle = 'red';
ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3);
ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls);

ctx.lineWidth = 1;
ctx.lineWidth = 1 * ls;
if (isHovered) {
ctx.fillStyle = 'magenta';
ctx.fillRect(this.p2.x - 1.5, this.p2.y - 1.5, 3, 3);
ctx.fillRect(this.p2.x - 1.5 * ls, this.p2.y - 1.5 * ls, 3 * ls, 3 * ls);
}
}

Expand Down
11 changes: 6 additions & 5 deletions simulator/js/objs/blocker/DiffractionGrating.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,11 @@ objTypes['DiffractionGrating'] = class extends LineObjMixin(BaseSceneObj) {

draw(canvasRenderer, isAboveLight, isHovered) {
const ctx = canvasRenderer.ctx;
const ls = canvasRenderer.lengthScale;

if (this.p1.x == this.p2.x && this.p1.y == this.p2.y) {
ctx.fillStyle = 'rgb(128,128,128)';
ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3);
ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls);
return;
}

Expand All @@ -66,19 +67,19 @@ objTypes['DiffractionGrating'] = class extends LineObjMixin(BaseSceneObj) {
ctx.stroke();
}
ctx.strokeStyle = getMouseStyle(this, 'rgb(124,62,18)');
ctx.lineWidth = 2;
ctx.lineWidth = 2 * ls;
ctx.lineCap = 'butt';
ctx.beginPath();
if (this.customBrightness) {
ctx.setLineDash([2, 2]);
ctx.setLineDash([2 * ls, 2 * ls]);
} else {
ctx.setLineDash([4 * (1 - this.slitRatio), 4 * this.slitRatio]);
ctx.setLineDash([4 * (1 - this.slitRatio) * ls, 4 * this.slitRatio * ls]);
}
ctx.moveTo(this.p1.x, this.p1.y);
ctx.lineTo(this.p2.x, this.p2.y);
ctx.stroke();
ctx.setLineDash([]);
ctx.lineWidth = 1;
ctx.lineWidth = 1 * ls;
}

onSimulationStart() {
Expand Down
5 changes: 3 additions & 2 deletions simulator/js/objs/glass/CircleGlass.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@ objTypes['CircleGlass'] = class extends CircleObjMixin(BaseGlass) {

draw(canvasRenderer, isAboveLight, isHovered) {
const ctx = canvasRenderer.ctx;
const ls = canvasRenderer.lengthScale;

ctx.beginPath();
ctx.arc(this.p1.x, this.p1.y, geometry.segmentLength(this), 0, Math.PI * 2, false);
this.fillGlass(canvasRenderer, isAboveLight, isHovered);
ctx.lineWidth = 1;
ctx.fillStyle = 'red';
ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3);
ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls);
if (isHovered) {
ctx.fillStyle = 'magenta';
ctx.fillRect(this.p2.x - 1.5, this.p2.y - 1.5, 3, 3);
ctx.fillRect(this.p2.x - 1.5 * ls, this.p2.y - 1.5 * ls, 3 * ls, 3 * ls);
}
}

Expand Down
Loading

0 comments on commit 4061d91

Please sign in to comment.