From 4061d915e406caafe25a0d220fb7169464d4fe54 Mon Sep 17 00:00:00 2001 From: Yi-Ting Tu Date: Sun, 21 Jul 2024 20:30:23 -0400 Subject: [PATCH] Add `lengthScale` option --- simulator/js/CanvasRenderer.js | 15 ++++-- simulator/js/Mouse.js | 2 +- simulator/js/Scene.js | 12 +++-- simulator/js/editor.js | 11 ++-- simulator/js/index.js | 14 ++--- simulator/js/objs/BaseGlass.js | 3 +- simulator/js/objs/BaseGrinGlass.js | 2 +- simulator/js/objs/CircleObjMixin.js | 2 +- simulator/js/objs/blocker/Aperture.js | 13 ++--- simulator/js/objs/blocker/Blocker.js | 7 +-- simulator/js/objs/blocker/CircleBlocker.js | 10 ++-- .../js/objs/blocker/DiffractionGrating.js | 11 ++-- simulator/js/objs/glass/CircleGlass.js | 5 +- simulator/js/objs/glass/CircleGrinGlass.js | 7 +-- simulator/js/objs/glass/CustomGlass.js | 23 ++++---- simulator/js/objs/glass/Glass.js | 52 ++++++++++--------- simulator/js/objs/glass/GrinGlass.js | 17 +++--- simulator/js/objs/glass/IdealLens.js | 21 ++++---- simulator/js/objs/glass/PlaneGlass.js | 7 +-- simulator/js/objs/glass/SphericalLens.js | 13 ++--- simulator/js/objs/lightSource/AngleSource.js | 8 +-- simulator/js/objs/lightSource/Beam.js | 15 +++--- simulator/js/objs/lightSource/PointSource.js | 6 ++- simulator/js/objs/lightSource/SingleRay.js | 6 ++- simulator/js/objs/mirror/ArcMirror.js | 26 ++++++---- simulator/js/objs/mirror/BeamSplitter.js | 6 ++- simulator/js/objs/mirror/CustomMirror.js | 22 ++++---- simulator/js/objs/mirror/IdealMirror.js | 15 +++--- simulator/js/objs/mirror/Mirror.js | 4 +- simulator/js/objs/mirror/ParabolicMirror.js | 23 ++++---- simulator/js/objs/other/Detector.js | 21 ++++---- simulator/js/objs/other/Drawing.js | 28 +++++++--- simulator/js/objs/other/LineArrow.js | 8 ++- simulator/js/objs/other/Protractor.js | 26 ++++++---- simulator/js/objs/other/Ruler.js | 19 ++++--- simulator/js/objs/other/TextLabel.js | 2 + simulator/js/objs/special/CropBox.js | 12 +++-- simulator/js/objs/special/Handle.js | 19 ++++--- simulator/js/objs/special/ModuleObj.js | 8 ++- simulator/js/simulator.js | 39 +++++++------- 40 files changed, 322 insertions(+), 238 deletions(-) diff --git a/simulator/js/CanvasRenderer.js b/simulator/js/CanvasRenderer.js index eae876ec..b3cb3fd5 100644 --- a/simulator/js/CanvasRenderer.js +++ b/simulator/js/CanvasRenderer.js @@ -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; @@ -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; @@ -22,7 +25,7 @@ class CanvasRenderer { /** @property {boolean} isSVG - Whether the canvas is being exported to SVG. */ this.isSVG = ctx.constructor === C2S; } - + /** * Draw a point. * @param {Point} p @@ -30,7 +33,7 @@ class CanvasRenderer { */ 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); } /** @@ -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); @@ -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); @@ -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); @@ -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; diff --git a/simulator/js/Mouse.js b/simulator/js/Mouse.js index 4e00cff3..23494633 100644 --- a/simulator/js/Mouse.js +++ b/simulator/js/Mouse.js @@ -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; diff --git a/simulator/js/Scene.js b/simulator/js/Scene.js index cadb255a..16bc2f7a 100644 --- a/simulator/js/Scene.js +++ b/simulator/js/Scene.js @@ -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. @@ -39,6 +40,7 @@ class Scene { lockObjs: false, gridSize: 20, observer: null, + lengthScale: 1, origin: { x: 0, y: 0 }, scale: 1, width: 1500, @@ -46,12 +48,12 @@ class Scene { 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 }), () => { }); } /** @@ -135,7 +137,7 @@ class Scene { } this[key] = jsonData[key]; } - + // Rescale the scene to fit the current viewport. let rescaleFactor = 1; @@ -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; diff --git a/simulator/js/editor.js b/simulator/js/editor.js index 182c9f7c..8bbcc761 100644 --- a/simulator/js/editor.js +++ b/simulator/js/editor.js @@ -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 @@ -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); diff --git a/simulator/js/index.js b/simulator/js/index.js index 00fd3259..b6541aa3 100644 --- a/simulator/js/index.js +++ b/simulator/js/index.js @@ -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; @@ -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; @@ -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); @@ -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(); } diff --git a/simulator/js/objs/BaseGlass.js b/simulator/js/objs/BaseGlass.js index db66d12b..ba104cba 100644 --- a/simulator/js/objs/BaseGlass.js +++ b/simulator/js/objs/BaseGlass.js @@ -34,6 +34,7 @@ class BaseGlass extends BaseSceneObj { */ fillGlass(canvasRenderer, isAboveLight, isHovered) { const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; const n = this.refIndex; @@ -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(); } } diff --git a/simulator/js/objs/BaseGrinGlass.js b/simulator/js/objs/BaseGrinGlass.js index 1d2ed27d..91d2b106 100644 --- a/simulator/js/objs/BaseGrinGlass.js +++ b/simulator/js/objs/BaseGrinGlass.js @@ -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')); } diff --git a/simulator/js/objs/CircleObjMixin.js b/simulator/js/objs/CircleObjMixin.js index 4db9683d..36505b3c 100644 --- a/simulator/js/objs/CircleObjMixin.js +++ b/simulator/js/objs/CircleObjMixin.js @@ -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]); } diff --git a/simulator/js/objs/blocker/Aperture.js b/simulator/js/objs/blocker/Aperture.js index 48605c7f..9976edf8 100644 --- a/simulator/js/objs/blocker/Aperture.js +++ b/simulator/js/objs/blocker/Aperture.js @@ -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)); @@ -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); @@ -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); } } diff --git a/simulator/js/objs/blocker/Blocker.js b/simulator/js/objs/blocker/Blocker.js index ad8892aa..470c7ea1 100644 --- a/simulator/js/objs/blocker/Blocker.js +++ b/simulator/js/objs/blocker/Blocker.js @@ -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) { diff --git a/simulator/js/objs/blocker/CircleBlocker.js b/simulator/js/objs/blocker/CircleBlocker.js index b092173a..354f73f2 100644 --- a/simulator/js/objs/blocker/CircleBlocker.js +++ b/simulator/js/objs/blocker/CircleBlocker.js @@ -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); } } diff --git a/simulator/js/objs/blocker/DiffractionGrating.js b/simulator/js/objs/blocker/DiffractionGrating.js index 410798c7..9e9b0f5c 100644 --- a/simulator/js/objs/blocker/DiffractionGrating.js +++ b/simulator/js/objs/blocker/DiffractionGrating.js @@ -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; } @@ -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() { diff --git a/simulator/js/objs/glass/CircleGlass.js b/simulator/js/objs/glass/CircleGlass.js index ebc58bd2..a903cbb7 100644 --- a/simulator/js/objs/glass/CircleGlass.js +++ b/simulator/js/objs/glass/CircleGlass.js @@ -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); } } diff --git a/simulator/js/objs/glass/CircleGrinGlass.js b/simulator/js/objs/glass/CircleGrinGlass.js index f93f498f..0e0d06b8 100644 --- a/simulator/js/objs/glass/CircleGrinGlass.js +++ b/simulator/js/objs/glass/CircleGrinGlass.js @@ -23,16 +23,17 @@ objTypes['CircleGrinGlass'] = class extends CircleObjMixin(BaseGrinGlass) { 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.lineWidth = 1 * ls; 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); } } diff --git a/simulator/js/objs/glass/CustomGlass.js b/simulator/js/objs/glass/CustomGlass.js index 84aa3b09..24a7984f 100644 --- a/simulator/js/objs/glass/CustomGlass.js +++ b/simulator/js/objs/glass/CustomGlass.js @@ -36,10 +36,11 @@ objTypes['CustomGlass'] = class extends LineObjMixin(BaseGlass) { 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; } @@ -56,8 +57,8 @@ objTypes['CustomGlass'] = class extends LineObjMixin(BaseGlass) { } catch (e) { delete this.path; ctx.fillStyle = "red" - ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3); - ctx.fillRect(this.p2.x - 1.5, this.p2.y - 1.5, 3, 3); + ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls); + ctx.fillRect(this.p2.x - 1.5 * ls, this.p2.y - 1.5 * ls, 3 * ls, 3 * ls); this.error = e.toString(); return; } @@ -73,8 +74,8 @@ objTypes['CustomGlass'] = class extends LineObjMixin(BaseGlass) { var i; var lastError = ""; var hasPoints = false; - for (i = -0.1; i < p12d + 0.09; i += 0.1) { - var ix = i + 0.05; + for (i = -0.1 * this.scene.lengthScale; i < p12d + 0.09 * this.scene.lengthScale; i += 0.1 * this.scene.lengthScale) { + var ix = i + 0.05 * this.scene.lengthScale; if (ix < 0) ix = 0; if (ix > p12d) ix = p12d; var x = ix - x0; @@ -96,8 +97,8 @@ objTypes['CustomGlass'] = class extends LineObjMixin(BaseGlass) { if (!hasPoints || lastError.startsWith("Curve generation error:")) { delete this.path; ctx.fillStyle = "red" - ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3); - ctx.fillRect(this.p2.x - 1.5, this.p2.y - 1.5, 3, 3); + ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls); + ctx.fillRect(this.p2.x - 1.5 * ls, this.p2.y - 1.5 * ls, 3 * ls, 3 * ls); this.error = lastError.toString(); return; } @@ -105,8 +106,8 @@ objTypes['CustomGlass'] = class extends LineObjMixin(BaseGlass) { if (isHovered) { ctx.fillStyle = 'rgb(255,0,0)'; - ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3); - ctx.fillRect(this.p2.x - 1.5, this.p2.y - 1.5, 3, 3); + ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls); + ctx.fillRect(this.p2.x - 1.5 * ls, this.p2.y - 1.5 * ls, 3 * ls, 3 * ls); } this.drawGlass(canvasRenderer, isAboveLight, isHovered); @@ -164,13 +165,13 @@ objTypes['CustomGlass'] = class extends LineObjMixin(BaseGlass) { //Line segment i->i+1 var rp_temp = geometry.linesIntersection(geometry.line(ray.p1, ray.p2), geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])); - if (geometry.intersectionIsOnSegment(rp_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])) && geometry.intersectionIsOnRay(rp_temp, ray) && geometry.distanceSquared(ray.p1, rp_temp) > minShotLength_squared) { + if (geometry.intersectionIsOnSegment(rp_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])) && geometry.intersectionIsOnRay(rp_temp, ray) && geometry.distanceSquared(ray.p1, rp_temp) > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { s_lensq_temp = geometry.distanceSquared(ray.p1, rp_temp); s_point_temp = rp_temp; } if (s_point_temp) { - if (s_point && geometry.distanceSquared(s_point_temp, s_point) < minShotLength_squared && s_point_index != i - 1) { + if (s_point && geometry.distanceSquared(s_point_temp, s_point) < minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale && s_point_index != i - 1) { // The ray shots on a point where the upper and the lower surfaces overlap. return; } else if (s_lensq_temp < s_lensq) { diff --git a/simulator/js/objs/glass/Glass.js b/simulator/js/objs/glass/Glass.js index a8fbfce5..75caed52 100644 --- a/simulator/js/objs/glass/Glass.js +++ b/simulator/js/objs/glass/Glass.js @@ -19,6 +19,8 @@ objTypes['Glass'] = class extends BaseGlass { draw(canvasRenderer, isAboveLight, isHovered) { const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; + var p1; var p2; var p3; @@ -34,7 +36,7 @@ objTypes['Glass'] = class extends BaseGlass { if (this.path.length === 2 && this.path[0].x === this.path[1].x && this.path[0].y === this.path[1].y) { ctx.fillStyle = 'rgb(255,0,0)'; - ctx.fillRect(this.path[0].x - 1.5, this.path[0].y - 1.5, 3, 3); + ctx.fillRect(this.path[0].x - 1.5 * ls, this.path[0].y - 1.5 * ls, 3 * ls, 3 * ls); return; } ctx.beginPath(); @@ -64,7 +66,7 @@ objTypes['Glass'] = class extends BaseGlass { } ctx.globalAlpha = 1; ctx.strokeStyle = 'rgb(128,128,128)'; - ctx.lineWidth = 1; + ctx.lineWidth = 1 * ls; ctx.stroke(); } else { // The user has completed drawing the object @@ -102,10 +104,10 @@ objTypes['Glass'] = class extends BaseGlass { if (typeof this.path[i].arc != 'undefined') { if (this.path[i].arc) { ctx.fillStyle = 'rgb(255,0,255)'; - ctx.fillRect(this.path[i].x - 1.5, this.path[i].y - 1.5, 3, 3); + ctx.fillRect(this.path[i].x - 1.5 * ls, this.path[i].y - 1.5 * ls, 3 * ls, 3 * ls); } else { ctx.fillStyle = 'rgb(255,0,0)'; - ctx.fillRect(this.path[i].x - 1.5, this.path[i].y - 1.5, 3, 3); + ctx.fillRect(this.path[i].x - 1.5 * ls, this.path[i].y - 1.5 * ls, 3 * ls, 3 * ls); } } } @@ -143,7 +145,7 @@ objTypes['Glass'] = class extends BaseGlass { if (!this.notDone) { return; } const mousePos = mouse.getPosSnappedToGrid(); if (typeof this.path[this.path.length - 1].arc != 'undefined') { - if (this.path[this.path.length - 1].arc && Math.sqrt(Math.pow(this.path[this.path.length - 1].x - mousePos.x, 2) + Math.pow(this.path[this.path.length - 1].y - mousePos.y, 2)) >= 5) { + if (this.path[this.path.length - 1].arc && Math.sqrt(Math.pow(this.path[this.path.length - 1].x - mousePos.x, 2) + Math.pow(this.path[this.path.length - 1].y - mousePos.y, 2)) >= 5 * this.scene.lengthScale) { this.path[this.path.length] = mousePos; } } else { @@ -317,14 +319,14 @@ objTypes['Glass'] = class extends BaseGlass { r = geometry.distance(center, p3); rp_temp = geometry.lineCircleIntersections(geometry.line(ray.p1, ray.p2), geometry.circle(center, p2)); for (var ii = 1; ii <= 2; ii++) { - rp_exist[ii] = !geometry.intersectionIsOnSegment(geometry.linesIntersection(geometry.line(p1, p2), geometry.line(p3, rp_temp[ii])), geometry.line(p3, rp_temp[ii])) && geometry.intersectionIsOnRay(rp_temp[ii], ray) && geometry.distanceSquared(rp_temp[ii], ray.p1) > minShotLength_squared; + rp_exist[ii] = !geometry.intersectionIsOnSegment(geometry.linesIntersection(geometry.line(p1, p2), geometry.line(p3, rp_temp[ii])), geometry.line(p3, rp_temp[ii])) && geometry.intersectionIsOnRay(rp_temp[ii], ray) && geometry.distanceSquared(rp_temp[ii], ray.p1) > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale; rp_lensq[ii] = geometry.distanceSquared(ray.p1, rp_temp[ii]); } - if (rp_exist[1] && ((!rp_exist[2]) || rp_lensq[1] < rp_lensq[2]) && rp_lensq[1] > minShotLength_squared) { + if (rp_exist[1] && ((!rp_exist[2]) || rp_lensq[1] < rp_lensq[2]) && rp_lensq[1] > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { s_point_temp = rp_temp[1]; s_lensq_temp = rp_lensq[1]; } - if (rp_exist[2] && ((!rp_exist[1]) || rp_lensq[2] < rp_lensq[1]) && rp_lensq[2] > minShotLength_squared) { + if (rp_exist[2] && ((!rp_exist[1]) || rp_lensq[2] < rp_lensq[1]) && rp_lensq[2] > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { s_point_temp = rp_temp[2]; s_lensq_temp = rp_lensq[2]; } @@ -332,7 +334,7 @@ objTypes['Glass'] = class extends BaseGlass { // The three points on the arc is colinear. Treat as a line segment. var rp_temp = geometry.linesIntersection(geometry.line(ray.p1, ray.p2), geometry.line(this.path[i % this.path.length], this.path[(i + 2) % this.path.length])); - if (geometry.intersectionIsOnSegment(rp_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 2) % this.path.length])) && geometry.intersectionIsOnRay(rp_temp, ray) && geometry.distanceSquared(ray.p1, rp_temp) > minShotLength_squared) { + if (geometry.intersectionIsOnSegment(rp_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 2) % this.path.length])) && geometry.intersectionIsOnRay(rp_temp, ray) && geometry.distanceSquared(ray.p1, rp_temp) > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { s_lensq_temp = geometry.distanceSquared(ray.p1, rp_temp); s_point_temp = rp_temp; } @@ -341,7 +343,7 @@ objTypes['Glass'] = class extends BaseGlass { //Line segment i->i+1 var rp_temp = geometry.linesIntersection(geometry.line(ray.p1, ray.p2), geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])); - if (geometry.intersectionIsOnSegment(rp_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])) && geometry.intersectionIsOnRay(rp_temp, ray) && geometry.distanceSquared(ray.p1, rp_temp) > minShotLength_squared) { + if (geometry.intersectionIsOnSegment(rp_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])) && geometry.intersectionIsOnRay(rp_temp, ray) && geometry.distanceSquared(ray.p1, rp_temp) > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { s_lensq_temp = geometry.distanceSquared(ray.p1, rp_temp); s_point_temp = rp_temp; } @@ -387,7 +389,7 @@ objTypes['Glass'] = class extends BaseGlass { return this.getIncidentData(ray).incidentType; } - + /* Utility function */ getIncidentData(ray) { @@ -441,14 +443,14 @@ objTypes['Glass'] = class extends BaseGlass { rp2_temp = geometry.lineCircleIntersections(geometry.line(ray2.p1, ray2.p2), geometry.circle(center, p2)); for (var ii = 1; ii <= 2; ii++) { rp_on_ray[ii] = geometry.intersectionIsOnRay(rp_temp[ii], ray); - rp_exist[ii] = rp_on_ray[ii] && !geometry.intersectionIsOnSegment(geometry.linesIntersection(geometry.line(p1, p2), geometry.line(p3, rp_temp[ii])), geometry.line(p3, rp_temp[ii])) && geometry.distanceSquared(rp_temp[ii], ray.p1) > minShotLength_squared; + rp_exist[ii] = rp_on_ray[ii] && !geometry.intersectionIsOnSegment(geometry.linesIntersection(geometry.line(p1, p2), geometry.line(p3, rp_temp[ii])), geometry.line(p3, rp_temp[ii])) && geometry.distanceSquared(rp_temp[ii], ray.p1) > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale; rp_lensq[ii] = geometry.distanceSquared(ray.p1, rp_temp[ii]); - rp2_exist[ii] = !geometry.intersectionIsOnSegment(geometry.linesIntersection(geometry.line(p1, p2), geometry.line(p3, rp2_temp[ii])), geometry.line(p3, rp2_temp[ii])) && geometry.intersectionIsOnRay(rp2_temp[ii], ray2) && geometry.distanceSquared(rp2_temp[ii], ray2.p1) > minShotLength_squared; + rp2_exist[ii] = !geometry.intersectionIsOnSegment(geometry.linesIntersection(geometry.line(p1, p2), geometry.line(p3, rp2_temp[ii])), geometry.line(p3, rp2_temp[ii])) && geometry.intersectionIsOnRay(rp2_temp[ii], ray2) && geometry.distanceSquared(rp2_temp[ii], ray2.p1) > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale; rp2_lensq[ii] = geometry.distanceSquared(ray2.p1, rp2_temp[ii]); } - if (rp_exist[1] && ((!rp_exist[2]) || rp_lensq[1] < rp_lensq[2]) && rp_lensq[1] > minShotLength_squared) { + if (rp_exist[1] && ((!rp_exist[2]) || rp_lensq[1] < rp_lensq[2]) && rp_lensq[1] > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { s_point_temp = rp_temp[1]; s_lensq_temp = rp_lensq[1]; if (rp_on_ray[2] && rp_lensq[1] < rp_lensq[2]) { @@ -460,7 +462,7 @@ objTypes['Glass'] = class extends BaseGlass { normal_y_temp = center.y - s_point_temp.y; } } - if (rp_exist[2] && ((!rp_exist[1]) || rp_lensq[2] < rp_lensq[1]) && rp_lensq[2] > minShotLength_squared) { + if (rp_exist[2] && ((!rp_exist[1]) || rp_lensq[2] < rp_lensq[1]) && rp_lensq[2] > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { s_point_temp = rp_temp[2]; s_lensq_temp = rp_lensq[2]; if (rp_on_ray[1] && rp_lensq[2] < rp_lensq[1]) { @@ -472,15 +474,15 @@ objTypes['Glass'] = class extends BaseGlass { normal_y_temp = center.y - s_point_temp.y; } } - if (rp2_exist[1] && rp2_lensq[1] > minShotLength_squared) { + if (rp2_exist[1] && rp2_lensq[1] > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { ray_intersect_count++; } - if (rp2_exist[2] && rp2_lensq[2] > minShotLength_squared) { + if (rp2_exist[2] && rp2_lensq[2] > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { ray_intersect_count++; } // Test if too close to an edge - if (s_point_temp && (geometry.distanceSquared(s_point_temp, p1) < minShotLength_squared || geometry.distanceSquared(s_point_temp, p2) < minShotLength_squared)) { + if (s_point_temp && (geometry.distanceSquared(s_point_temp, p1) < minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale || geometry.distanceSquared(s_point_temp, p2) < minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale)) { nearEdge_temp = true; } @@ -489,7 +491,7 @@ objTypes['Glass'] = class extends BaseGlass { rp_temp = geometry.linesIntersection(geometry.line(ray.p1, ray.p2), geometry.line(this.path[i % this.path.length], this.path[(i + 2) % this.path.length])); rp2_temp = geometry.linesIntersection(geometry.line(ray2.p1, ray2.p2), geometry.line(this.path[i % this.path.length], this.path[(i + 2) % this.path.length])); - if (geometry.intersectionIsOnSegment(rp_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 2) % this.path.length])) && geometry.intersectionIsOnRay(rp_temp, ray) && geometry.distanceSquared(ray.p1, rp_temp) > minShotLength_squared) { + if (geometry.intersectionIsOnSegment(rp_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 2) % this.path.length])) && geometry.intersectionIsOnRay(rp_temp, ray) && geometry.distanceSquared(ray.p1, rp_temp) > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { s_lensq_temp = geometry.distanceSquared(ray.p1, rp_temp); s_point_temp = rp_temp; @@ -502,12 +504,12 @@ objTypes['Glass'] = class extends BaseGlass { } - if (geometry.intersectionIsOnSegment(rp2_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 2) % this.path.length])) && geometry.intersectionIsOnRay(rp2_temp, ray2) && geometry.distanceSquared(ray2.p1, rp2_temp) > minShotLength_squared) { + if (geometry.intersectionIsOnSegment(rp2_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 2) % this.path.length])) && geometry.intersectionIsOnRay(rp2_temp, ray2) && geometry.distanceSquared(ray2.p1, rp2_temp) > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { ray_intersect_count++; } // Test if too close to an edge - if (s_point_temp && (geometry.distanceSquared(s_point_temp, this.path[i % this.path.length]) < minShotLength_squared || geometry.distanceSquared(s_point_temp, this.path[(i + 2) % this.path.length]) < minShotLength_squared)) { + if (s_point_temp && (geometry.distanceSquared(s_point_temp, this.path[i % this.path.length]) < minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale || geometry.distanceSquared(s_point_temp, this.path[(i + 2) % this.path.length]) < minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale)) { nearEdge_temp = true; } } @@ -516,7 +518,7 @@ objTypes['Glass'] = class extends BaseGlass { rp_temp = geometry.linesIntersection(geometry.line(ray.p1, ray.p2), geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])); rp2_temp = geometry.linesIntersection(geometry.line(ray2.p1, ray2.p2), geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])); - if (geometry.intersectionIsOnSegment(rp_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])) && geometry.intersectionIsOnRay(rp_temp, ray) && geometry.distanceSquared(ray.p1, rp_temp) > minShotLength_squared) { + if (geometry.intersectionIsOnSegment(rp_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])) && geometry.intersectionIsOnRay(rp_temp, ray) && geometry.distanceSquared(ray.p1, rp_temp) > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { s_lensq_temp = geometry.distanceSquared(ray.p1, rp_temp); s_point_temp = rp_temp; @@ -529,17 +531,17 @@ objTypes['Glass'] = class extends BaseGlass { } - if (geometry.intersectionIsOnSegment(rp2_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])) && geometry.intersectionIsOnRay(rp2_temp, ray2) && geometry.distanceSquared(ray2.p1, rp2_temp) > minShotLength_squared) { + if (geometry.intersectionIsOnSegment(rp2_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])) && geometry.intersectionIsOnRay(rp2_temp, ray2) && geometry.distanceSquared(ray2.p1, rp2_temp) > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { ray_intersect_count++; } // Test if too close to an edge - if (s_point_temp && (geometry.distanceSquared(s_point_temp, this.path[i % this.path.length]) < minShotLength_squared || geometry.distanceSquared(s_point_temp, this.path[(i + 1) % this.path.length]) < minShotLength_squared)) { + if (s_point_temp && (geometry.distanceSquared(s_point_temp, this.path[i % this.path.length]) < minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale || geometry.distanceSquared(s_point_temp, this.path[(i + 1) % this.path.length]) < minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale)) { nearEdge_temp = true; } } if (s_point_temp) { - if (s_point && geometry.distanceSquared(s_point_temp, s_point) < minShotLength_squared) { + if (s_point && geometry.distanceSquared(s_point_temp, s_point) < minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { // Self surface merging surfaceMultiplicity++; } else if (s_lensq_temp < s_lensq) { diff --git a/simulator/js/objs/glass/GrinGlass.js b/simulator/js/objs/glass/GrinGlass.js index 540ef0ee..08088350 100644 --- a/simulator/js/objs/glass/GrinGlass.js +++ b/simulator/js/objs/glass/GrinGlass.js @@ -23,11 +23,12 @@ objTypes['GrinGlass'] = class extends BaseGrinGlass { draw(canvasRenderer, isAboveLight, isHovered) { const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; if (this.notDone) { if (this.path.length === 2 && this.path[0].x === this.path[1].x && this.path[0].y === this.path[1].y) { ctx.fillStyle = 'rgb(255,0,0)'; - ctx.fillRect(this.path[0].x - 1.5, this.path[0].y - 1.5, 3, 3); + ctx.fillRect(this.path[0].x - 1.5 * ls, this.path[0].y - 1.5 * ls, 3 * ls, 3 * ls); return; } @@ -40,7 +41,7 @@ objTypes['GrinGlass'] = class extends BaseGrinGlass { } ctx.globalAlpha = 1; ctx.strokeStyle = 'rgb(128,128,128)'; - ctx.lineWidth = 1; + ctx.lineWidth = 1 * ls; ctx.stroke(); } else { // The user has completed drawing the object @@ -58,7 +59,7 @@ objTypes['GrinGlass'] = class extends BaseGrinGlass { if (isHovered) { for (var i = 0; i < this.path.length; i++) { ctx.fillStyle = 'rgb(255,0,0)'; - ctx.fillRect(this.path[i].x - 1.5, this.path[i].y - 1.5, 3, 3); + ctx.fillRect(this.path[i].x - 1.5 * ls, this.path[i].y - 1.5 * ls, 3 * ls, 3 * ls); } } } @@ -187,7 +188,7 @@ objTypes['GrinGlass'] = class extends BaseGrinGlass { //Line segment i->i+1 var rp_temp = geometry.linesIntersection(geometry.line(ray.p1, ray.p2), geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])); - if (geometry.intersectionIsOnSegment(rp_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])) && geometry.intersectionIsOnRay(rp_temp, ray) && geometry.distanceSquared(ray.p1, rp_temp) > minShotLength_squared) { + if (geometry.intersectionIsOnSegment(rp_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])) && geometry.intersectionIsOnRay(rp_temp, ray) && geometry.distanceSquared(ray.p1, rp_temp) > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { s_lensq_temp = geometry.distanceSquared(ray.p1, rp_temp); s_point_temp = rp_temp; @@ -319,7 +320,7 @@ objTypes['GrinGlass'] = class extends BaseGrinGlass { rp_temp = geometry.linesIntersection(geometry.line(ray.p1, ray.p2), geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])); rp2_temp = geometry.linesIntersection(geometry.line(ray2.p1, ray2.p2), geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])); - if (geometry.intersectionIsOnSegment(rp_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])) && geometry.intersectionIsOnRay(rp_temp, ray) && geometry.distanceSquared(ray.p1, rp_temp) > minShotLength_squared) { + if (geometry.intersectionIsOnSegment(rp_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])) && geometry.intersectionIsOnRay(rp_temp, ray) && geometry.distanceSquared(ray.p1, rp_temp) > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { s_lensq_temp = geometry.distanceSquared(ray.p1, rp_temp); s_point_temp = rp_temp; @@ -331,17 +332,17 @@ objTypes['GrinGlass'] = class extends BaseGrinGlass { } - if (geometry.intersectionIsOnSegment(rp2_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])) && geometry.intersectionIsOnRay(rp2_temp, ray2) && geometry.distanceSquared(ray2.p1, rp2_temp) > minShotLength_squared) { + if (geometry.intersectionIsOnSegment(rp2_temp, geometry.line(this.path[i % this.path.length], this.path[(i + 1) % this.path.length])) && geometry.intersectionIsOnRay(rp2_temp, ray2) && geometry.distanceSquared(ray2.p1, rp2_temp) > minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { ray_intersect_count++; } // Test if too close to an edge - if (s_point_temp && (geometry.distanceSquared(s_point_temp, this.path[i % this.path.length]) < minShotLength_squared || geometry.distanceSquared(s_point_temp, this.path[(i + 1) % this.path.length]) < minShotLength_squared)) { + if (s_point_temp && (geometry.distanceSquared(s_point_temp, this.path[i % this.path.length]) < minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale || geometry.distanceSquared(s_point_temp, this.path[(i + 1) % this.path.length]) < minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale)) { nearEdge_temp = true; } if (s_point_temp) { - if (s_point && geometry.distanceSquared(s_point_temp, s_point) < minShotLength_squared) { + if (s_point && geometry.distanceSquared(s_point_temp, s_point) < minShotLength_squared * this.scene.lengthScale * this.scene.lengthScale) { // Self surface merging surfaceMultiplicity++; } else if (s_lensq_temp < s_lensq) { diff --git a/simulator/js/objs/glass/IdealLens.js b/simulator/js/objs/glass/IdealLens.js index 308c73cd..8ae33968 100644 --- a/simulator/js/objs/glass/IdealLens.js +++ b/simulator/js/objs/glass/IdealLens.js @@ -15,17 +15,18 @@ objTypes['IdealLens'] = class extends LineObjMixin(BaseSceneObj) { }; populateObjBar(objBar) { - objBar.createNumber(getMsg('focalLength'), -1000, 1000, 1, this.focalLength, function (obj, value) { + objBar.createNumber(getMsg('focalLength'), -1000 * this.scene.lengthScale, 1000 * this.scene.lengthScale, 1 * this.scene.lengthScale, this.focalLength, function (obj, value) { obj.focalLength = value; }); } 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; } @@ -35,19 +36,19 @@ objTypes['IdealLens'] = class extends LineObjMixin(BaseSceneObj) { var per_x = par_y; var per_y = -par_x; - var arrow_size_per = 5; - var arrow_size_par = 5; - var center_size = 2; + var arrow_size_per = 5 * ls; + var arrow_size_par = 5 * ls; + var center_size = 2 * ls; // Draw the line segment ctx.strokeStyle = isHovered ? 'cyan' : ('rgb(128,128,128)'); - ctx.globalAlpha = 1 / ((Math.abs(this.focalLength) / 100) + 1); - ctx.lineWidth = 4; + ctx.globalAlpha = 1 / ((Math.abs(this.focalLength / this.scene.lengthScale) / 100) + 1); + ctx.lineWidth = 4 * ls; 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; ctx.globalAlpha = 1; ctx.fillStyle = 'rgb(255,0,0)'; @@ -96,8 +97,8 @@ objTypes['IdealLens'] = class extends LineObjMixin(BaseSceneObj) { // show focal length var mp = geometry.segmentMidpoint(this); ctx.fillStyle = 'rgb(255,0,255)'; - ctx.fillRect(mp.x + this.focalLength * per_x - 1.5, mp.y + this.focalLength * per_y - 1.5, 3, 3); - ctx.fillRect(mp.x - this.focalLength * per_x - 1.5, mp.y - this.focalLength * per_y - 1.5, 3, 3); + ctx.fillRect(mp.x + this.focalLength * per_x - 1.5 * ls, mp.y + this.focalLength * per_y - 1.5 * ls, 3 * ls, 3 * ls); + ctx.fillRect(mp.x - this.focalLength * per_x - 1.5 * ls, mp.y - this.focalLength * per_y - 1.5 * ls, 3 * ls, 3 * ls); } } diff --git a/simulator/js/objs/glass/PlaneGlass.js b/simulator/js/objs/glass/PlaneGlass.js index c459689c..4df88add 100644 --- a/simulator/js/objs/glass/PlaneGlass.js +++ b/simulator/js/objs/glass/PlaneGlass.js @@ -19,10 +19,11 @@ objTypes['PlaneGlass'] = class extends LineObjMixin(BaseGlass) { 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; } @@ -44,8 +45,8 @@ objTypes['PlaneGlass'] = class extends LineObjMixin(BaseGlass) { if (isHovered) { ctx.fillStyle = 'magenta'; - ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3); - ctx.fillRect(this.p2.x - 1.5, this.p2.y - 1.5, 3, 3); + ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls); + ctx.fillRect(this.p2.x - 1.5 * ls, this.p2.y - 1.5 * ls, 3 * ls, 3 * ls); } } diff --git a/simulator/js/objs/glass/SphericalLens.js b/simulator/js/objs/glass/SphericalLens.js index ec589274..337f74c3 100644 --- a/simulator/js/objs/glass/SphericalLens.js +++ b/simulator/js/objs/glass/SphericalLens.js @@ -130,18 +130,19 @@ objTypes['SphericalLens'] = class extends objTypes['Glass'] { draw(canvasRenderer, isAboveLight, isHovered) { const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; if (this.p1 && this.p2 && 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; } if (!this.path) { if (this.p1 && this.p2) { ctx.fillStyle = 'rgb(255,0,0)'; - ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3); - ctx.fillRect(this.p2.x - 1.5, this.p2.y - 1.5, 3, 3); + ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls); + ctx.fillRect(this.p2.x - 1.5 * ls, this.p2.y - 1.5 * ls, 3 * ls, 3 * ls); } return; } @@ -163,8 +164,8 @@ objTypes['SphericalLens'] = class extends objTypes['Glass'] { var bfd = params.bfd; ctx.fillStyle = 'rgb(255,0,255)'; - ctx.fillRect(this.path[2].x + bfd * dpx - 1.5, this.path[2].y + bfd * dpy - 1.5, 3, 3); - ctx.fillRect(this.path[5].x - ffd * dpx - 1.5, this.path[5].y - ffd * dpy - 1.5, 3, 3); + ctx.fillRect(this.path[2].x + bfd * dpx - 1.5 * ls, this.path[2].y + bfd * dpy - 1.5 * ls, 3 * ls, 3 * ls); + ctx.fillRect(this.path[5].x - ffd * dpx - 1.5 * ls, this.path[5].y - ffd * dpy - 1.5 * ls, 3 * ls, 3 * ls); } } @@ -345,7 +346,7 @@ objTypes['SphericalLens'] = class extends objTypes['Glass'] { var dpy = -dx; var cx = (p1.x + p2.x) * .5; var cy = (p1.y + p2.y) * .5; - const thick = 10; + const thick = 10 * this.scene.lengthScale; // create lens if (!this.path) this.path = []; this.path[0] = { x: p1.x - dpx * thick, y: p1.y - dpy * thick, arc: false }; diff --git a/simulator/js/objs/lightSource/AngleSource.js b/simulator/js/objs/lightSource/AngleSource.js index c04534d2..a3810079 100644 --- a/simulator/js/objs/lightSource/AngleSource.js +++ b/simulator/js/objs/lightSource/AngleSource.js @@ -41,14 +41,16 @@ objTypes['AngleSource'] = class extends LineObjMixin(BaseSceneObj) { draw(canvasRenderer, isAboveLight, isHovered) { const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; + ctx.fillStyle = isHovered ? 'cyan' : (this.scene.simulateColors ? wavelengthToColor(this.wavelength, 1) : 'rgb(0,255,0)'); - ctx.fillRect(this.p1.x - 2.5, this.p1.y - 2.5, 5, 5); + ctx.fillRect(this.p1.x - 2.5 * ls, this.p1.y - 2.5 * ls, 5 * ls, 5 * ls); if (this.scene.simulateColors) { ctx.fillStyle = isHovered ? 'cyan' : ('rgb(255,255,255)'); - 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.fillStyle = isHovered ? 'cyan' : ('rgb(255,0,0)'); - 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); } onSimulationStart() { diff --git a/simulator/js/objs/lightSource/Beam.js b/simulator/js/objs/lightSource/Beam.js index 579fb2b5..c3efdb68 100644 --- a/simulator/js/objs/lightSource/Beam.js +++ b/simulator/js/objs/lightSource/Beam.js @@ -48,29 +48,30 @@ objTypes['Beam'] = 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; } var a_l = Math.atan2(this.p1.x - this.p2.x, this.p1.y - this.p2.y) - Math.PI / 2; ctx.strokeStyle = isHovered ? 'cyan' : (this.scene.simulateColors ? wavelengthToColor(this.wavelength, 1) : 'rgb(0,255,0)'); - ctx.lineWidth = 4; + ctx.lineWidth = 4 * ls; ctx.lineCap = 'butt'; ctx.beginPath(); - ctx.moveTo(this.p1.x + Math.sin(a_l) * 2, this.p1.y + Math.cos(a_l) * 2); - ctx.lineTo(this.p2.x + Math.sin(a_l) * 2, this.p2.y + Math.cos(a_l) * 2); + ctx.moveTo(this.p1.x + Math.sin(a_l) * 2 * ls, this.p1.y + Math.cos(a_l) * 2 * ls); + ctx.lineTo(this.p2.x + Math.sin(a_l) * 2 * ls, this.p2.y + Math.cos(a_l) * 2 * ls); ctx.stroke(); ctx.strokeStyle = 'rgba(128,128,128,255)'; - ctx.lineWidth = 2; + ctx.lineWidth = 2 * ls; 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; ctx.lineCap = 'butt'; } @@ -81,7 +82,7 @@ objTypes['Beam'] = class extends LineObjMixin(BaseSceneObj) { this.warning = null; } - var n = geometry.segmentLength(this) * this.scene.rayDensity; + var n = geometry.segmentLength(this) * this.scene.rayDensity / this.scene.lengthScale; var stepX = (this.p2.x - this.p1.x) / n; var stepY = (this.p2.y - this.p1.y) / n; var s = Math.PI * 2 / parseInt(this.scene.rayDensity * 500); diff --git a/simulator/js/objs/lightSource/PointSource.js b/simulator/js/objs/lightSource/PointSource.js index 9940d6f8..6e87c7cd 100644 --- a/simulator/js/objs/lightSource/PointSource.js +++ b/simulator/js/objs/lightSource/PointSource.js @@ -29,11 +29,13 @@ objTypes['PointSource'] = class extends BaseSceneObj { draw(canvasRenderer, isAboveLight, isHovered) { const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; + ctx.fillStyle = this.scene.simulateColors ? wavelengthToColor(this.wavelength, 1) : isHovered ? 'cyan' : ('rgb(0,255,0)'); - ctx.fillRect(this.x - 2.5, this.y - 2.5, 5, 5); + ctx.fillRect(this.x - 2.5 * ls, this.y - 2.5 * ls, 5 * ls, 5 * ls); if (this.scene.simulateColors) { ctx.fillStyle = isHovered ? 'cyan' : ('rgb(255,255,255)'); - ctx.fillRect(this.x - 1.5, this.y - 1.5, 3, 3); + ctx.fillRect(this.x - 1.5 * ls, this.y - 1.5 * ls, 3 * ls, 3 * ls); } } diff --git a/simulator/js/objs/lightSource/SingleRay.js b/simulator/js/objs/lightSource/SingleRay.js index 6aa7aac7..aaecdc6d 100644 --- a/simulator/js/objs/lightSource/SingleRay.js +++ b/simulator/js/objs/lightSource/SingleRay.js @@ -29,10 +29,12 @@ objTypes['SingleRay'] = class extends LineObjMixin(BaseSceneObj) { draw(canvasRenderer, isAboveLight, isHovered) { const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; + ctx.fillStyle = isHovered ? 'cyan' : (this.scene.simulateColors ? wavelengthToColor(this.wavelength, 1) : 'rgb(255,0,0)'); - ctx.fillRect(this.p1.x - 2.5, this.p1.y - 2.5, 5, 5); + ctx.fillRect(this.p1.x - 2.5 * ls, this.p1.y - 2.5 * ls, 5 * ls, 5 * ls); ctx.fillStyle = isHovered ? 'cyan' : ('rgb(255,0,0)'); - 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); } onSimulationStart() { diff --git a/simulator/js/objs/mirror/ArcMirror.js b/simulator/js/objs/mirror/ArcMirror.js index 5b479c18..f514d32f 100644 --- a/simulator/js/objs/mirror/ArcMirror.js +++ b/simulator/js/objs/mirror/ArcMirror.js @@ -24,6 +24,8 @@ objTypes['ArcMirror'] = class extends BaseFilter { draw(canvasRenderer, isAboveLight, isHovered) { const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; + ctx.fillStyle = 'rgb(255,0,255)'; if (this.p3 && this.p2) { var center = geometry.linesIntersection(geometry.perpendicularBisector(geometry.line(this.p1, this.p3)), geometry.perpendicularBisector(geometry.line(this.p2, this.p3))); @@ -33,35 +35,37 @@ objTypes['ArcMirror'] = class extends BaseFilter { var a2 = Math.atan2(this.p2.y - center.y, this.p2.x - center.x); var a3 = Math.atan2(this.p3.y - center.y, this.p3.x - center.x); ctx.strokeStyle = isHovered ? 'cyan' : ((scene.simulateColors && this.wavelength && this.filter) ? wavelengthToColor(this.wavelength || GREEN_WAVELENGTH, 1) : 'rgb(168,168,168)'); + ctx.lineWidth = 1 * ls; ctx.beginPath(); ctx.arc(center.x, center.y, r, a1, a2, (a2 < a3 && a3 < a1) || (a1 < a2 && a2 < a3) || (a3 < a1 && a1 < a2)); ctx.stroke(); if (isHovered) { - ctx.fillRect(this.p3.x - 1.5, this.p3.y - 1.5, 3, 3); + ctx.fillRect(this.p3.x - 1.5 * ls, this.p3.y - 1.5 * ls, 3 * ls, 3 * ls); ctx.fillStyle = 'rgb(255,0,0)'; - ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3); - ctx.fillRect(this.p2.x - 1.5, this.p2.y - 1.5, 3, 3); + ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls); + ctx.fillRect(this.p2.x - 1.5 * ls, this.p2.y - 1.5 * ls, 3 * ls, 3 * ls); } } else { // The three points on the arc is colinear. Treat as a line segment. ctx.strokeStyle = isHovered ? 'cyan' : ((scene.simulateColors && this.wavelength && this.filter) ? wavelengthToColor(this.wavelength || GREEN_WAVELENGTH, 1) : 'rgb(168,168,168)'); + ctx.lineWidth = 1 * ls; ctx.beginPath(); ctx.moveTo(this.p1.x, this.p1.y); ctx.lineTo(this.p2.x, this.p2.y); ctx.stroke(); - ctx.fillRect(this.p3.x - 1.5, this.p3.y - 1.5, 3, 3); + ctx.fillRect(this.p3.x - 1.5 * ls, this.p3.y - 1.5 * ls, 3 * ls, 3 * ls); ctx.fillStyle = 'rgb(255,0,0)'; - ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3); - ctx.fillRect(this.p2.x - 1.5, this.p2.y - 1.5, 3, 3); + ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls); + ctx.fillRect(this.p2.x - 1.5 * ls, this.p2.y - 1.5 * ls, 3 * ls, 3 * ls); } } else if (this.p2) { ctx.fillStyle = 'rgb(255,0,0)'; - ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3); - ctx.fillRect(this.p2.x - 1.5, this.p2.y - 1.5, 3, 3); + ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls); + ctx.fillRect(this.p2.x - 1.5 * ls, this.p2.y - 1.5 * ls, 3 * ls, 3 * ls); } else { ctx.fillStyle = 'rgb(255,0,0)'; - 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); } } @@ -232,7 +236,7 @@ objTypes['ArcMirror'] = class extends BaseFilter { var rp_exist = []; var rp_lensq = []; for (var i = 1; i <= 2; i++) { - rp_exist[i] = !geometry.intersectionIsOnSegment(geometry.linesIntersection(geometry.line(this.p1, this.p2), geometry.line(this.p3, rp_temp[i])), geometry.line(this.p3, rp_temp[i])) && geometry.intersectionIsOnRay(rp_temp[i], ray) && geometry.distanceSquared(rp_temp[i], ray.p1) > minShotLength_squared; + rp_exist[i] = !geometry.intersectionIsOnSegment(geometry.linesIntersection(geometry.line(this.p1, this.p2), geometry.line(this.p3, rp_temp[i])), geometry.line(this.p3, rp_temp[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]); } if (rp_exist[1] && ((!rp_exist[2]) || rp_lensq[1] < rp_lensq[2])) { return rp_temp[1]; } @@ -266,7 +270,7 @@ objTypes['ArcMirror'] = class extends BaseFilter { ray.p2 = geometry.point(incidentPoint.x - c_sq * rx + 2 * r_dot_c * cx, incidentPoint.y - c_sq * ry + 2 * r_dot_c * cy); } else { // The three points on the arc is colinear. Treat as a line segment. - + var rx = ray.p1.x - incidentPoint.x; var ry = ray.p1.y - incidentPoint.y; var mx = this.p2.x - this.p1.x; diff --git a/simulator/js/objs/mirror/BeamSplitter.js b/simulator/js/objs/mirror/BeamSplitter.js index 3aa314ea..5f56a43f 100644 --- a/simulator/js/objs/mirror/BeamSplitter.js +++ b/simulator/js/objs/mirror/BeamSplitter.js @@ -32,20 +32,22 @@ objTypes['BeamSplitter'] = 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' : ('rgb(100,100,168)'); + ctx.lineWidth = 1 * ls; ctx.beginPath(); ctx.moveTo(this.p1.x, this.p1.y); ctx.lineTo(this.p2.x, this.p2.y); ctx.stroke(); ctx.strokeStyle = isHovered ? 'cyan' : ((scene.simulateColors && this.wavelength && this.filter) ? wavelengthToColor(this.wavelength || GREEN_WAVELENGTH, 1) : 'rgb(100,100,168)'); - ctx.setLineDash([15, 15]); + ctx.setLineDash([15 * ls, 15 * ls]); ctx.moveTo(this.p1.x, this.p1.y); ctx.lineTo(this.p2.x, this.p2.y); ctx.stroke(); diff --git a/simulator/js/objs/mirror/CustomMirror.js b/simulator/js/objs/mirror/CustomMirror.js index 3a143b2d..6cf371d0 100644 --- a/simulator/js/objs/mirror/CustomMirror.js +++ b/simulator/js/objs/mirror/CustomMirror.js @@ -34,10 +34,11 @@ objTypes['CustomMirror'] = 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; } @@ -47,8 +48,8 @@ objTypes['CustomMirror'] = class extends LineObjMixin(BaseFilter) { } catch (e) { delete this.tmp_points; ctx.fillStyle = "red" - ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3); - ctx.fillRect(this.p2.x - 1.5, this.p2.y - 1.5, 3, 3); + ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls); + ctx.fillRect(this.p2.x - 1.5 * ls, this.p2.y - 1.5 * ls, 3 * ls, 3 * ls); this.error = e.toString(); return; } @@ -62,12 +63,13 @@ objTypes['CustomMirror'] = class extends LineObjMixin(BaseFilter) { var x0 = p12d / 2; var i; ctx.strokeStyle = isHovered ? 'cyan' : ((scene.simulateColors && this.wavelength && this.filter) ? wavelengthToColor(this.wavelength || GREEN_WAVELENGTH, 1) : 'rgb(168,168,168)'); + ctx.lineWidth = 1 * ls; ctx.beginPath(); this.tmp_points = []; var lastError = ""; - for (i = -0.1; i < p12d + 0.09; i += 0.1) { + for (i = -0.1 * this.scene.lengthScale; i < p12d + 0.09 * this.scene.lengthScale; i += 0.1 * this.scene.lengthScale) { // avoid using exact integers to avoid problems with detecting intersections - var ix = i + 0.05; + var ix = i + 0.05 * this.scene.lengthScale; if (ix < 0) ix = 0; if (ix > p12d) ix = p12d; var x = ix - x0; @@ -90,16 +92,16 @@ objTypes['CustomMirror'] = class extends LineObjMixin(BaseFilter) { if (this.tmp_points.length == 0) { delete this.tmp_points; ctx.fillStyle = "red" - ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3); - ctx.fillRect(this.p2.x - 1.5, this.p2.y - 1.5, 3, 3); + ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls); + ctx.fillRect(this.p2.x - 1.5 * ls, this.p2.y - 1.5 * ls, 3 * ls, 3 * ls); this.error = lastError.toString(); return; } ctx.stroke(); if (isHovered) { ctx.fillStyle = 'rgb(255,0,0)'; - ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3); - ctx.fillRect(this.p2.x - 1.5, this.p2.y - 1.5, 3, 3); + ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls); + ctx.fillRect(this.p2.x - 1.5 * ls, this.p2.y - 1.5 * ls, 3 * ls, 3 * ls); } this.error = null; @@ -147,7 +149,7 @@ objTypes['CustomMirror'] = class extends LineObjMixin(BaseFilter) { var rp_temp = geometry.linesIntersection(geometry.line(ray.p1, ray.p2), geometry.line(pts[i], pts[i + 1])); var seg = geometry.line(pts[i], pts[i + 1]); // need minShotLength check to handle a ray that reflects off mirror multiple times - if (geometry.distance(ray.p1, rp_temp) < minShotLength) + if (geometry.distance(ray.p1, rp_temp) < minShotLength * this.scene.lengthScale) continue; if (geometry.intersectionIsOnSegment(rp_temp, seg) && geometry.intersectionIsOnRay(rp_temp, ray)) { if (!incidentPoint || geometry.distance(ray.p1, rp_temp) < geometry.distance(ray.p1, incidentPoint)) { diff --git a/simulator/js/objs/mirror/IdealMirror.js b/simulator/js/objs/mirror/IdealMirror.js index 18026565..6ea5b1b7 100644 --- a/simulator/js/objs/mirror/IdealMirror.js +++ b/simulator/js/objs/mirror/IdealMirror.js @@ -24,7 +24,7 @@ objTypes['IdealMirror'] = class extends LineObjMixin(BaseFilter) { if (localStorage && localStorage.rayOpticsCartesianSign) { cartesianSign = localStorage.rayOpticsCartesianSign == "true"; } - objBar.createNumber(getMsg('focalLength'), -1000, 1000, 1, this.focalLength * (cartesianSign ? -1 : 1), function (obj, value) { + objBar.createNumber(getMsg('focalLength'), -1000 * this.scene.lengthScale, 1000 * this.scene.lengthScale, 1 * this.scene.lengthScale, this.focalLength * (cartesianSign ? -1 : 1), function (obj, value) { obj.focalLength = value * (cartesianSign ? -1 : 1); }); if (objBar.showAdvanced(cartesianSign)) { @@ -38,10 +38,11 @@ objTypes['IdealMirror'] = 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; } @@ -51,19 +52,19 @@ objTypes['IdealMirror'] = class extends LineObjMixin(BaseFilter) { var per_x = par_y; var per_y = -par_x; - var arrow_size_per = 5; - var arrow_size_par = 5; - var center_size = 1; + var arrow_size_per = 5 * ls; + var arrow_size_par = 5 * ls; + var center_size = 1 * ls; // Draw the line segment ctx.strokeStyle = isHovered ? 'cyan' : ((scene.simulateColors && this.wavelength && this.filter) ? wavelengthToColor(this.wavelength || GREEN_WAVELENGTH, 1) : 'rgb(168,168,168)'); ctx.globalAlpha = 1; - ctx.lineWidth = 1; + ctx.lineWidth = 1 * ls; 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; // Draw the center point of the mirror diff --git a/simulator/js/objs/mirror/Mirror.js b/simulator/js/objs/mirror/Mirror.js index 6ab51b67..ba21afe1 100644 --- a/simulator/js/objs/mirror/Mirror.js +++ b/simulator/js/objs/mirror/Mirror.js @@ -22,14 +22,16 @@ objTypes['Mirror'] = 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(168,168,168)'); + ctx.lineWidth = 1 * ls; ctx.beginPath(); ctx.moveTo(this.p1.x, this.p1.y); ctx.lineTo(this.p2.x, this.p2.y); diff --git a/simulator/js/objs/mirror/ParabolicMirror.js b/simulator/js/objs/mirror/ParabolicMirror.js index 75a665bf..60451ae5 100644 --- a/simulator/js/objs/mirror/ParabolicMirror.js +++ b/simulator/js/objs/mirror/ParabolicMirror.js @@ -27,6 +27,8 @@ objTypes['ParabolicMirror'] = class extends BaseFilter { draw(canvasRenderer, isAboveLight, isHovered) { const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; + ctx.fillStyle = 'rgb(255,0,255)'; if (this.p3 && this.p2) { var p12d = geometry.distance(this.p1, this.p2); @@ -43,12 +45,13 @@ objTypes['ParabolicMirror'] = class extends BaseFilter { var a = height / (x0 * x0); // y=ax^2 var i; ctx.strokeStyle = isHovered ? 'cyan' : ((scene.simulateColors && this.wavelength && this.filter) ? wavelengthToColor(this.wavelength || GREEN_WAVELENGTH, 1) : 'rgb(168,168,168)'); + ctx.lineWidth = 1 * ls; ctx.beginPath(); this.tmp_points = [geometry.point(this.p1.x, this.p1.y)]; ctx.moveTo(this.p1.x, this.p1.y); - for (i = 0.1; i < p12d; i += 0.1) { + for (i = 0.1 * this.scene.lengthScale; i < p12d; i += 0.1 * this.scene.lengthScale) { // avoid using exact integers to avoid problems with detecting intersections - var ix = i + .001; + var ix = i + .001 * this.scene.lengthScale; var x = ix - x0; var y = height - a * x * x; var pt = geometry.point(this.p1.x + dir1[0] * ix + dir2[0] * y, this.p1.y + dir1[1] * ix + dir2[1] * y); @@ -57,21 +60,21 @@ objTypes['ParabolicMirror'] = class extends BaseFilter { } ctx.stroke(); if (isHovered) { - ctx.fillRect(this.p3.x - 1.5, this.p3.y - 1.5, 3, 3); + ctx.fillRect(this.p3.x - 1.5 * ls, this.p3.y - 1.5 * ls, 3 * ls, 3 * ls); var focusx = (this.p1.x + this.p2.x) * .5 + dir2[0] * (height - 1 / (4 * a)); var focusy = (this.p1.y + this.p2.y) * .5 + dir2[1] * (height - 1 / (4 * a)); - ctx.fillRect(focusx - 1.5, focusy - 1.5, 3, 3); + ctx.fillRect(focusx - 1.5 * ls, focusy - 1.5 * ls, 3 * ls, 3 * ls); ctx.fillStyle = 'rgb(255,0,0)'; - ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3); - ctx.fillRect(this.p2.x - 1.5, this.p2.y - 1.5, 3, 3); + ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls); + ctx.fillRect(this.p2.x - 1.5 * ls, this.p2.y - 1.5 * ls, 3 * ls, 3 * ls); } } else if (this.p2) { ctx.fillStyle = 'rgb(255,0,0)'; - ctx.fillRect(this.p1.x - 1.5, this.p1.y - 1.5, 3, 3); - ctx.fillRect(this.p2.x - 1.5, this.p2.y - 1.5, 3, 3); + ctx.fillRect(this.p1.x - 1.5 * ls, this.p1.y - 1.5 * ls, 3 * ls, 3 * ls); + ctx.fillRect(this.p2.x - 1.5 * ls, this.p2.y - 1.5 * ls, 3 * ls, 3 * ls); } else { ctx.fillStyle = 'rgb(255,0,0)'; - 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); } } @@ -233,7 +236,7 @@ objTypes['ParabolicMirror'] = class extends BaseFilter { var rp_temp = geometry.linesIntersection(geometry.line(ray.p1, ray.p2), geometry.line(pts[i], pts[i + 1])); var seg = geometry.line(pts[i], pts[i + 1]); // need minShotLength check to handle a ray that reflects off mirror multiple times - if (geometry.distance(ray.p1, rp_temp) < minShotLength) + if (geometry.distance(ray.p1, rp_temp) < minShotLength * this.scene.lengthScale) continue; if (geometry.intersectionIsOnSegment(rp_temp, seg) && geometry.intersectionIsOnRay(rp_temp, ray)) { if (!incidentPoint || geometry.distance(ray.p1, rp_temp) < geometry.distance(ray.p1, incidentPoint)) { diff --git a/simulator/js/objs/other/Detector.js b/simulator/js/objs/other/Detector.js index b7b64cb3..5e3e07c4 100644 --- a/simulator/js/objs/other/Detector.js +++ b/simulator/js/objs/other/Detector.js @@ -36,7 +36,7 @@ objTypes['Detector'] = class extends LineObjMixin(BaseSceneObj) { }, null, true); if (this.irradMap) { - objBar.createNumber(getMsg('binSize'), 0.01, 10, 0.01, this.binSize, function (obj, value) { + objBar.createNumber(getMsg('binSize'), 0.01 * this.scene.lengthScale, 10 * this.scene.lengthScale, 0.01 * this.scene.lengthScale, this.binSize, function (obj, value) { obj.binSize = value; }); @@ -70,11 +70,14 @@ objTypes['Detector'] = class extends LineObjMixin(BaseSceneObj) { draw(canvasRenderer, isAboveLight, isHovered) { const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; + if (!isAboveLight) { ctx.globalCompositeOperation = 'lighter'; - ctx.strokeStyle = isHovered ? 'cyan' : ('rgb(192,192,192)') - ctx.setLineDash([5, 5]); + ctx.strokeStyle = isHovered ? 'cyan' : ('rgb(192,192,192)'); + ctx.lineWidth = 1 * ls; + ctx.setLineDash([5 * ls, 5 * ls]); ctx.beginPath(); ctx.moveTo(this.p1.x, this.p1.y); ctx.lineTo(this.p2.x, this.p2.y); @@ -97,13 +100,13 @@ objTypes['Detector'] = class extends LineObjMixin(BaseSceneObj) { var str3 = "F∥=" + this.shear.toFixed(2); } - ctx.font = '16px Arial'; + ctx.font = (16 * ls) + 'px Arial'; ctx.textAlign = 'right'; ctx.textBaseline = 'top'; ctx.fillStyle = isHovered ? 'cyan' : ('rgb(192,192,192)'); ctx.fillText(str1, this.p2.x, this.p2.y); - ctx.fillText(str2, this.p2.x, this.p2.y + 20); - ctx.fillText(str3, this.p2.x, this.p2.y + 40); + ctx.fillText(str2, this.p2.x, this.p2.y + 20 * ls); + ctx.fillText(str3, this.p2.x, this.p2.y + 40 * ls); ctx.globalCompositeOperation = 'source-over'; if (this.irradMap && this.binData) { @@ -115,14 +118,14 @@ objTypes['Detector'] = class extends LineObjMixin(BaseSceneObj) { var vy = -ux; // Draw the irradiance map - ctx.lineWidth = 1; + ctx.lineWidth = 1 * ls; ctx.strokeStyle = isHovered ? 'cyan' : ('rgb(255,255,255)'); ctx.fillStyle = 'blue'; ctx.beginPath(); ctx.moveTo(this.p1.x, this.p1.y); for (var i = 0; i < this.binData.length; i++) { - ctx.lineTo(this.p1.x + ux * i * this.binSize + vx * this.binData[i] / this.binSize * 20, this.p1.y + uy * i * this.binSize + vy * this.binData[i] / this.binSize * 20); - ctx.lineTo(this.p1.x + ux * (i + 1) * this.binSize + vx * this.binData[i] / this.binSize * 20, this.p1.y + uy * (i + 1) * this.binSize + vy * this.binData[i] / this.binSize * 20); + ctx.lineTo(this.p1.x + ux * i * this.binSize + vx * this.binData[i] / this.binSize * 20 * ls * ls, this.p1.y + uy * i * this.binSize + vy * this.binData[i] / this.binSize * 20 * ls * ls); + ctx.lineTo(this.p1.x + ux * (i + 1) * this.binSize + vx * this.binData[i] / this.binSize * 20 * ls * ls, this.p1.y + uy * (i + 1) * this.binSize + vy * this.binData[i] / this.binSize * 20 * ls * ls); } ctx.lineTo(this.p2.x, this.p2.y); ctx.fill(); diff --git a/simulator/js/objs/other/Drawing.js b/simulator/js/objs/other/Drawing.js index baa9e104..96a0b4f5 100644 --- a/simulator/js/objs/other/Drawing.js +++ b/simulator/js/objs/other/Drawing.js @@ -1,7 +1,7 @@ /** * Drawing tool * Tools -> Other -> Drawing - * @property {Array>} strokes - The strokes of the drawing. Each element represents a stroke, which is an array of coordinates ordered as `[x1, y1, x2, y2, ...]`. The coordinates are rounded to integers to reduce the size of the JSON data. + * @property {Array>} strokes - The strokes of the drawing. Each element represents a stroke, which is an array of coordinates ordered as `[x1, y1, x2, y2, ...]`. The coordinates are rounded to reduce the size of the JSON data. * @property {boolean} isDrawing - Whether the user is drawing (before "stop drawing" is clicked). * @property {boolean} isMouseDown - Temperary indication of whether the mouse is down (during the drawing stage). */ @@ -23,7 +23,10 @@ objTypes['Drawing'] = class extends BaseSceneObj { draw(canvasRenderer, isAboveLight, isHovered) { const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; + ctx.strokeStyle = isHovered ? 'cyan' : 'white'; + ctx.lineWidth = 1 * ls; ctx.beginPath(); for (const stroke of this.strokes) { ctx.moveTo(stroke[0], stroke[1]); @@ -35,8 +38,8 @@ objTypes['Drawing'] = class extends BaseSceneObj { } move(diffX, diffY) { - let roundedDiffX = Math.round(diffX); - let roundedDiffY = Math.round(diffY); + let roundedDiffX = this.round(diffX); + let roundedDiffY = this.round(diffY); for (const stroke of this.strokes) { for (let i = 0; i < stroke.length; i += 2) { stroke[i] += roundedDiffX; @@ -52,7 +55,7 @@ objTypes['Drawing'] = class extends BaseSceneObj { this.strokes = []; } const mousePos = mouse.getPosSnappedToGrid(); - this.strokes.push([Math.round(mousePos.x), Math.round(mousePos.y)]); + this.strokes.push([this.round(mousePos.x), this.round(mousePos.y)]); this.isMouseDown = true; } @@ -63,9 +66,9 @@ objTypes['Drawing'] = class extends BaseSceneObj { const distanceSq = (this.strokes[this.strokes.length - 1][this.strokes[this.strokes.length - 1].length - 2] - mousePos.x) ** 2 + (this.strokes[this.strokes.length - 1][this.strokes[this.strokes.length - 1].length - 1] - mousePos.y) ** 2; - if (distanceSq < 4) return; + if (distanceSq < 4 * this.scene.lengthScale * this.scene.lengthScale) return; - this.strokes[this.strokes.length - 1].push(Math.round(mousePos.x), Math.round(mousePos.y)); + this.strokes[this.strokes.length - 1].push(this.round(mousePos.x), this.round(mousePos.y)); } onConstructMouseUp(mouse, ctrl, shift) { @@ -88,7 +91,7 @@ objTypes['Drawing'] = class extends BaseSceneObj { for (let i = 0; i < stroke.length - 2; i += 2) { if (mouse.isOnSegment(geometry.line(geometry.point(stroke[i], stroke[i + 1]), geometry.point(stroke[i + 2], stroke[i + 3])))) { const mousePos = mouse.getPosSnappedToGrid(); - const roundedMousePos = geometry.point(Math.round(mousePos.x), Math.round(mousePos.y)); + const roundedMousePos = geometry.point(this.round(mousePos.x), this.round(mousePos.y)); dragContext.part = 0; dragContext.mousePos0 = roundedMousePos; // Mouse position when the user starts dragging dragContext.mousePos1 = roundedMousePos; // Mouse position at the last moment during dragging @@ -107,7 +110,7 @@ objTypes['Drawing'] = class extends BaseSceneObj { dragContext.snapContext = {}; // Unlock the dragging direction when the user release the shift key } - const roundedMousePos = geometry.point(Math.round(mousePos.x), Math.round(mousePos.y)); + const roundedMousePos = geometry.point(this.round(mousePos.x), this.round(mousePos.y)); var mouseDiffX = dragContext.mousePos1.x - roundedMousePos.x; // The X difference between the mouse position now and at the previous moment var mouseDiffY = dragContext.mousePos1.y - roundedMousePos.y; // The Y difference between the mouse position now and at the previous moment @@ -124,4 +127,13 @@ objTypes['Drawing'] = class extends BaseSceneObj { // Update the mouse position dragContext.mousePos1 = roundedMousePos; } + + /* Utility functions */ + + /** + * Round the coordinates of the strokes to integers times the length scale (to reduce the size of the JSON data). + */ + round(num) { + return Math.round(num / this.scene.lengthScale) * this.scene.lengthScale; + } }; diff --git a/simulator/js/objs/other/LineArrow.js b/simulator/js/objs/other/LineArrow.js index d857792e..25ee938c 100644 --- a/simulator/js/objs/other/LineArrow.js +++ b/simulator/js/objs/other/LineArrow.js @@ -27,14 +27,16 @@ objTypes['LineArrow'] = 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; } ctx.strokeStyle = isHovered ? 'cyan' : 'white'; + ctx.lineWidth = 1 * ls; ctx.beginPath(); ctx.moveTo(this.p1.x, this.p1.y); ctx.lineTo(this.p2.x, this.p2.y); @@ -52,8 +54,10 @@ objTypes['LineArrow'] = class extends LineObjMixin(BaseSceneObj) { drawArrow(canvasRenderer, p1, p2) { const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; + const angle = Math.atan2(p2.y - p1.y, p2.x - p1.x); - const len = 10; + const len = 10 * ls; ctx.beginPath(); ctx.moveTo(p2.x, p2.y); ctx.lineTo(p2.x - len * Math.cos(angle - Math.PI / 6), p2.y - len * Math.sin(angle - Math.PI / 6)); diff --git a/simulator/js/objs/other/Protractor.js b/simulator/js/objs/other/Protractor.js index 6e4dcf9d..eb0e7f31 100644 --- a/simulator/js/objs/other/Protractor.js +++ b/simulator/js/objs/other/Protractor.js @@ -13,20 +13,22 @@ objTypes['Protractor'] = class extends CircleObjMixin(BaseSceneObj) { draw(canvasRenderer, isAboveLight, isHovered) { const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; + if (!isAboveLight) { ctx.globalCompositeOperation = 'lighter'; var r = Math.sqrt((this.p2.x - this.p1.x) * (this.p2.x - this.p1.x) + (this.p2.y - this.p1.y) * (this.p2.y - this.p1.y)); - var scale_width_limit = 5; + var scale_width_limit = 5 * ls; var scale_step = 1; var scale_step_mid = 5; var scale_step_long = 10; - var scale_len = 10; - var scale_len_mid = 15; - var scale_len_long = 20; + var scale_len = 10 * ls; + var scale_len_mid = 15 * ls; + var scale_len_long = 20 * ls; ctx.strokeStyle = isHovered ? 'cyan' : ('rgb(128,128,128)'); - ctx.font = 'bold 14px Arial'; + ctx.font = 'bold ' + (14 * ls) + 'px Arial'; ctx.fillStyle = 'rgb(128,128,128)'; if (r * scale_step * Math.PI / 180 < scale_width_limit) { @@ -40,10 +42,10 @@ objTypes['Protractor'] = class extends CircleObjMixin(BaseSceneObj) { scale_step = 5; scale_step_mid = 10; scale_step_long = 30; - scale_len = 5; - scale_len_mid = 8; - scale_len_long = 10; - ctx.font = 'bold 10px Arial'; + scale_len = 5 * ls; + scale_len_mid = 8 * ls; + scale_len_long = 10 * ls; + ctx.font = 'bold ' + (10 * ls) + 'px Arial'; } if (r * scale_step * Math.PI / 180 < scale_width_limit) { // The scale is too small @@ -55,6 +57,8 @@ objTypes['Protractor'] = class extends CircleObjMixin(BaseSceneObj) { ctx.textAlign = 'center'; ctx.textBaseline = 'top'; + ctx.lineWidth = 1 * ls; + ctx.beginPath(); ctx.arc(this.p1.x, this.p1.y, r, 0, Math.PI * 2, false); @@ -83,10 +87,10 @@ objTypes['Protractor'] = class extends CircleObjMixin(BaseSceneObj) { ctx.globalCompositeOperation = 'source-over'; } 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 - 2.5, this.p2.y - 2.5, 5, 5); + ctx.fillRect(this.p2.x - 2.5 * ls, this.p2.y - 2.5 * ls, 5 * ls, 5 * ls); } } }; diff --git a/simulator/js/objs/other/Ruler.js b/simulator/js/objs/other/Ruler.js index e2deeea7..29cc44ec 100644 --- a/simulator/js/objs/other/Ruler.js +++ b/simulator/js/objs/other/Ruler.js @@ -23,10 +23,11 @@ objTypes['Ruler'] = class extends LineObjMixin(BaseSceneObj) { if (isAboveLight) return; 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; } @@ -41,42 +42,44 @@ objTypes['Ruler'] = class extends LineObjMixin(BaseSceneObj) { var scale_step = this.scaleInterval; var scale_step_mid = scale_step * 5; var scale_step_long = scale_step * 10; - var scale_len = 10; - var scale_len_mid = 15; + var scale_len = 10 * ls; + var scale_len_mid = 15 * ls; ctx.strokeStyle = isHovered ? 'cyan' : ('rgb(128,128,128)'); - ctx.font = '14px Arial'; + ctx.font = (14 * ls) + 'px Arial'; ctx.fillStyle = 'rgb(128,128,128)'; if (ang > Math.PI * (-0.25) && ang <= Math.PI * 0.25) { //↘~↗ var scale_direction = -1; - var scale_len_long = 20; + var scale_len_long = 20 * ls; var text_ang = ang; ctx.textAlign = 'center'; ctx.textBaseline = 'top'; } else if (ang > Math.PI * (-0.75) && ang <= Math.PI * (-0.25)) { //↗~↖ var scale_direction = 1; - var scale_len_long = 15; + var scale_len_long = 15 * ls; var text_ang = ang - Math.PI * (-0.5); ctx.textAlign = 'right'; ctx.textBaseline = 'middle'; } else if (ang > Math.PI * 0.75 || ang <= Math.PI * (-0.75)) { //↖~↙ var scale_direction = 1; - var scale_len_long = 20; + var scale_len_long = 20 * ls; var text_ang = ang - Math.PI; ctx.textAlign = 'center'; ctx.textBaseline = 'top'; } else { //↙~↘ var scale_direction = -1; - var scale_len_long = 15; + var scale_len_long = 15 * ls; var text_ang = ang - Math.PI * 0.5; ctx.textAlign = 'right'; ctx.textBaseline = 'middle'; } + ctx.lineWidth = 1 * ls; + ctx.beginPath(); ctx.moveTo(this.p1.x, this.p1.y); ctx.lineTo(this.p2.x, this.p2.y); diff --git a/simulator/js/objs/other/TextLabel.js b/simulator/js/objs/other/TextLabel.js index 5d5b28e4..1df80a05 100644 --- a/simulator/js/objs/other/TextLabel.js +++ b/simulator/js/objs/other/TextLabel.js @@ -99,6 +99,8 @@ objTypes['TextLabel'] = class extends BaseSceneObj { draw(canvasRenderer, isAboveLight, isHovered) { const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; + ctx.fillStyle = isHovered ? 'cyan' : ('white'); ctx.textAlign = this.alignment; ctx.textBaseline = 'bottom'; diff --git a/simulator/js/objs/special/CropBox.js b/simulator/js/objs/special/CropBox.js index 01e6e38d..eba155ba 100644 --- a/simulator/js/objs/special/CropBox.js +++ b/simulator/js/objs/special/CropBox.js @@ -103,8 +103,10 @@ objTypes['CropBox'] = class extends BaseSceneObj { if (!cropMode) return; const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; + ctx.strokeStyle = isHovered ? 'cyan' : 'white'; - ctx.lineWidth = 1; + ctx.lineWidth = 1 * ls; ctx.beginPath(); ctx.moveTo(this.p1.x, this.p1.y); ctx.lineTo(this.p2.x, this.p2.y); @@ -114,16 +116,16 @@ objTypes['CropBox'] = class extends BaseSceneObj { ctx.stroke(); ctx.fillStyle = isHovered ? 'cyan' : 'white'; ctx.beginPath(); - ctx.arc(this.p1.x, this.p1.y, 5, 0, 2 * Math.PI); + ctx.arc(this.p1.x, this.p1.y, 5 * ls, 0, 2 * Math.PI); ctx.fill(); ctx.beginPath(); - ctx.arc(this.p2.x, this.p2.y, 5, 0, 2 * Math.PI); + ctx.arc(this.p2.x, this.p2.y, 5 * ls, 0, 2 * Math.PI); ctx.fill(); ctx.beginPath(); - ctx.arc(this.p3.x, this.p3.y, 5, 0, 2 * Math.PI); + ctx.arc(this.p3.x, this.p3.y, 5 * ls, 0, 2 * Math.PI); ctx.fill(); ctx.beginPath(); - ctx.arc(this.p4.x, this.p4.y, 5, 0, 2 * Math.PI); + ctx.arc(this.p4.x, this.p4.y, 5 * ls, 0, 2 * Math.PI); ctx.fill(); } diff --git a/simulator/js/objs/special/Handle.js b/simulator/js/objs/special/Handle.js index 0e3bbbea..39c9d611 100644 --- a/simulator/js/objs/special/Handle.js +++ b/simulator/js/objs/special/Handle.js @@ -41,12 +41,15 @@ objTypes['Handle'] = class extends BaseSceneObj { draw(canvasRenderer, isAboveLight, isHovered) { const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; + + ctx.lineWidth = 1 * ls; for (var i in this.controlPoints) { ctx.globalAlpha = 1; ctx.beginPath(); ctx.strokeStyle = this.notDone ? 'cyan' : isHovered ? 'cyan' : ('gray'); - ctx.setLineDash([2, 2]); - ctx.arc(this.controlPoints[i].newPoint.x, this.controlPoints[i].newPoint.y, 5, 0, Math.PI * 2, false); + ctx.setLineDash([2 * ls, 2 * ls]); + ctx.arc(this.controlPoints[i].newPoint.x, this.controlPoints[i].newPoint.y, 5 * ls, 0, Math.PI * 2, false); ctx.stroke(); ctx.setLineDash([]); } @@ -54,18 +57,18 @@ objTypes['Handle'] = class extends BaseSceneObj { ctx.globalAlpha = 1; ctx.beginPath(); ctx.strokeStyle = isHovered ? 'cyan' : ('gray'); - ctx.arc(this.p1.x, this.p1.y, 2, 0, Math.PI * 2, false); + ctx.arc(this.p1.x, this.p1.y, 2 * ls, 0, Math.PI * 2, false); ctx.stroke(); ctx.beginPath(); - ctx.arc(this.p1.x, this.p1.y, 5, 0, Math.PI * 2, false); + ctx.arc(this.p1.x, this.p1.y, 5 * ls, 0, Math.PI * 2, false); ctx.stroke(); ctx.beginPath(); - ctx.moveTo(this.p2.x - 5, this.p2.y); - ctx.lineTo(this.p2.x + 5, this.p2.y); + ctx.moveTo(this.p2.x - 5 * ls, this.p2.y); + ctx.lineTo(this.p2.x + 5 * ls, this.p2.y); ctx.stroke(); ctx.beginPath(); - ctx.moveTo(this.p2.x, this.p2.y - 5); - ctx.lineTo(this.p2.x, this.p2.y + 5); + ctx.moveTo(this.p2.x, this.p2.y - 5 * ls); + ctx.lineTo(this.p2.x, this.p2.y + 5 * ls); ctx.stroke(); } } diff --git a/simulator/js/objs/special/ModuleObj.js b/simulator/js/objs/special/ModuleObj.js index 58282b79..efaa9dde 100644 --- a/simulator/js/objs/special/ModuleObj.js +++ b/simulator/js/objs/special/ModuleObj.js @@ -82,6 +82,9 @@ objTypes['ModuleObj'] = class extends BaseSceneObj { } draw(canvasRenderer, isAboveLight, isHovered) { + const ctx = canvasRenderer.ctx; + const ls = canvasRenderer.lengthScale; + // Sort the expanded objects with z-index. let mapped = this.objs.map(function(obj, i) { return {index: i, value: obj.getZIndex()}; @@ -96,13 +99,14 @@ objTypes['ModuleObj'] = class extends BaseSceneObj { } // Draw the control points + ctx.lineWidth = 1 * ls; for (let point of this.points) { ctx.beginPath(); ctx.strokeStyle = isHovered ? 'cyan' : ('gray'); - ctx.arc(point.x, point.y, 2, 0, Math.PI * 2, false); + ctx.arc(point.x, point.y, 2 * ls, 0, Math.PI * 2, false); ctx.stroke(); ctx.beginPath(); - ctx.arc(point.x, point.y, 5, 0, Math.PI * 2, false); + ctx.arc(point.x, point.y, 5 * ls, 0, Math.PI * 2, false); ctx.stroke(); } } diff --git a/simulator/js/simulator.js b/simulator/js/simulator.js index 1f81d5a2..497cbc30 100644 --- a/simulator/js/simulator.js +++ b/simulator/js/simulator.js @@ -61,8 +61,8 @@ function draw_(skipLight, skipGrid) { //JSONOutput(); if (ctx0.constructor != C2S) { - var canvasRenderer0 = new CanvasRenderer(ctx0, {x: scene.origin.x*dpr, y: scene.origin.y*dpr}, (scene.scale*dpr), scene.backgroundImage); - var canvasRenderer1 = new CanvasRenderer(ctx, {x: scene.origin.x*dpr, y: scene.origin.y*dpr}, (scene.scale*dpr)); + var canvasRenderer0 = new CanvasRenderer(ctx0, {x: scene.origin.x*dpr, y: scene.origin.y*dpr}, (scene.scale*dpr), scene.lengthScale, scene.backgroundImage); + var canvasRenderer1 = new CanvasRenderer(ctx, {x: scene.origin.x*dpr, y: scene.origin.y*dpr}, (scene.scale*dpr), scene.lengthScale); canvasRenderer0.clear(); canvasRenderer1.clear(); @@ -70,7 +70,7 @@ function draw_(skipLight, skipGrid) { if (!skipLight) { delete canvasRenderer; - canvasRenderer = new CanvasRenderer(ctxLight, {x: scene.origin.x*dpr, y: scene.origin.y*dpr}, (scene.scale*dpr)); + canvasRenderer = new CanvasRenderer(ctxLight, {x: scene.origin.x*dpr, y: scene.origin.y*dpr}, (scene.scale*dpr), scene.lengthScale); canvasRenderer.clear(); if (ctx0.constructor == C2S) { @@ -86,7 +86,7 @@ function draw_(skipLight, skipGrid) { if (!skipGrid && ctx0.constructor != C2S) { - var canvasRendererGrid = new CanvasRenderer(ctxGrid, {x: scene.origin.x*dpr, y: scene.origin.y*dpr}, (scene.scale*dpr)); + var canvasRendererGrid = new CanvasRenderer(ctxGrid, {x: scene.origin.x*dpr, y: scene.origin.y*dpr}, (scene.scale*dpr), scene.lengthScale); canvasRendererGrid.clear(); if (scene.showGrid) { @@ -94,9 +94,10 @@ function draw_(skipLight, skipGrid) { ctxGrid.save(); ctxGrid.setTransform((scene.scale*dpr), 0, 0, (scene.scale*dpr), 0, 0); - var dashstep = 4; + var dashstep = 4*scene.lengthScale; ctxGrid.strokeStyle = 'rgb(255,255,255,0.25)'; + ctxGrid.lineWidth = 1*scene.lengthScale; var dashPattern; if (dashstep * scene.scale <= 2) { @@ -293,7 +294,7 @@ function shootWaitingRays() { { // Here opticalObjs[i] intersects with the ray at s_point_temp s_lensq_temp = geometry.distanceSquared(waitingRays[j].p1, s_point_temp); - if (s_point && geometry.distanceSquared(s_point_temp, s_point) < minShotLength_squared && (opticalObjs[i].constructor.supportsSurfaceMerging || s_obj.constructor.supportsSurfaceMerging)) + if (s_point && geometry.distanceSquared(s_point_temp, s_point) < minShotLength_squared*scene.lengthScale*scene.lengthScale && (opticalObjs[i].constructor.supportsSurfaceMerging || s_obj.constructor.supportsSurfaceMerging)) { // The ray is shot on two objects at the same time, and at least one of them supports surface merging @@ -317,7 +318,7 @@ function shootWaitingRays() { } } } - else if (s_lensq_temp < s_lensq && s_lensq_temp > minShotLength_squared) + else if (s_lensq_temp < s_lensq && s_lensq_temp > minShotLength_squared*scene.lengthScale*scene.lengthScale) { s_obj = opticalObjs[i]; // Update the object to be shot s_obj_index = i; @@ -347,7 +348,7 @@ function shootWaitingRays() { if (scene.mode == 'extended' && !waitingRays[j].isNew) { if (scene.simulateColors) { - ctxLight.setLineDash([2, 2]); + ctxLight.setLineDash([2*scene.lengthScale, 2*scene.lengthScale]); canvasRenderer.drawRay(geometry.line(waitingRays[j].p1, geometry.point(waitingRays[j].p1.x * 2 - waitingRays[j].p2.x, waitingRays[j].p1.y * 2 - waitingRays[j].p2.y)), color); // Draw the extension of the ray ctxLight.setLineDash([]); } else { @@ -381,9 +382,9 @@ function shootWaitingRays() { if (scene.mode == 'extended' && !waitingRays[j].isNew) { if (scene.simulateColors) { - ctxLight.setLineDash([2, 2]); + ctxLight.setLineDash([2*scene.lengthScale, 2*scene.lengthScale]); canvasRenderer.drawRay(geometry.line(waitingRays[j].p1, geometry.point(waitingRays[j].p1.x * 2 - waitingRays[j].p2.x, waitingRays[j].p1.y * 2 - waitingRays[j].p2.y)), color); // Draw the backward extension of the ray - ctxLight.setLineDash([1, 5]); + ctxLight.setLineDash([1*scene.lengthScale, 5*scene.lengthScale]); canvasRenderer.drawRay(geometry.line(s_point, geometry.point(s_point.x * 2 - waitingRays[j].p1.x, s_point.y * 2 - waitingRays[j].p1.y)), color); // Draw the forward extension of the ray ctxLight.setLineDash([]); } else { @@ -415,10 +416,10 @@ function shootWaitingRays() { if (observed) { - if (last_intersection && geometry.distanceSquared(last_intersection, observed_intersection) < 25) + if (last_intersection && geometry.distanceSquared(last_intersection, observed_intersection) < 25*scene.lengthScale*scene.lengthScale) { // If the intersections are near each others - if (geometry.intersectionIsOnRay(observed_intersection, geometry.line(observed_point, waitingRays[j].p1)) && geometry.distanceSquared(observed_point, waitingRays[j].p1) > 1e-5) + if (geometry.intersectionIsOnRay(observed_intersection, geometry.line(observed_point, waitingRays[j].p1)) && geometry.distanceSquared(observed_point, waitingRays[j].p1) > 1e-5*scene.lengthScale*scene.lengthScale) { @@ -440,7 +441,7 @@ function shootWaitingRays() { // Virtual image if (scene.simulateColors) { ctxLight.fillStyle = color; - ctxLight.fillRect(observed_intersection.x - 1.5, observed_intersection.y - 1.5, 3, 3); + ctxLight.fillRect(observed_intersection.x - 1.5*scene.lengthScale, observed_intersection.y - 1.5*scene.lengthScale, 3*scene.lengthScale, 3*scene.lengthScale); } else { canvasRenderer.drawPoint(observed_intersection, 'rgb(255,128,0)'); // Draw the image } @@ -455,7 +456,7 @@ function shootWaitingRays() { } } if (scene.simulateColors) { - ctxLight.setLineDash([1, 2]); + ctxLight.setLineDash([1*scene.lengthScale, 2*scene.lengthScale]); canvasRenderer.drawSegment(geometry.line(observed_point, observed_intersection), color); // Draw the observed ray ctxLight.setLineDash([]); } else { @@ -465,7 +466,7 @@ function shootWaitingRays() { else { if (scene.simulateColors) { - ctxLight.setLineDash([1, 2]); + ctxLight.setLineDash([1*scene.lengthScale, 2*scene.lengthScale]); canvasRenderer.drawRay(geometry.line(observed_point, waitingRays[j].p1), color); // Draw the observed ray ctxLight.setLineDash([]); } else { @@ -478,7 +479,7 @@ function shootWaitingRays() { if (last_intersection) { if (scene.simulateColors) { - ctxLight.setLineDash([1, 2]); + ctxLight.setLineDash([1*scene.lengthScale, 2*scene.lengthScale]); canvasRenderer.drawRay(geometry.line(observed_point, waitingRays[j].p1), color); // Draw the observed ray ctxLight.setLineDash([]); } else { @@ -501,7 +502,7 @@ function shootWaitingRays() { { observed_intersection = geometry.linesIntersection(waitingRays[j], last_ray); - if (last_intersection && geometry.distanceSquared(last_intersection, observed_intersection) < 25) + if (last_intersection && geometry.distanceSquared(last_intersection, observed_intersection) < 25*scene.lengthScale*scene.lengthScale) { if (scene.simulateColors) { var color = wavelengthToColor(waitingRays[j].wavelength, (waitingRays[j].brightness_s + waitingRays[j].brightness_p) * 0.5, true); @@ -523,7 +524,7 @@ function shootWaitingRays() { // Virtual image if (scene.simulateColors) { ctxLight.fillStyle = color; - ctxLight.fillRect(observed_intersection.x - 1.5, observed_intersection.y - 1.5, 3, 3); + ctxLight.fillRect(observed_intersection.x - 1.5*scene.lengthScale, observed_intersection.y - 1.5*scene.lengthScale, 3*scene.lengthScale, 3*scene.lengthScale); } else { canvasRenderer.drawPoint(observed_intersection, 'rgb(255,128,0)'); // Draw the image } @@ -542,7 +543,7 @@ function shootWaitingRays() { // Virtual object if (scene.simulateColors) { ctxLight.fillStyle = color; - ctxLight.fillRect(observed_intersection.x - 0.5, observed_intersection.y - 0.5, 1, 1); + ctxLight.fillRect(observed_intersection.x - 0.5*scene.lengthScale, observed_intersection.y - 0.5*scene.lengthScale, 1*scene.lengthScale, 1*scene.lengthScale); } else { canvasRenderer.drawPoint(observed_intersection, 'rgb(80,80,80)'); }