WikiMSK:Sandbox/box10: Difference between revisions

From WikiMSK
No edit summary
No edit summary
 
Line 1: Line 1:
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Image Viewer</title>
  <style>
    #container {
      position: relative;
      display: inline-block;
      height: 100%;
    }
    #image {
      display: block;
      max-width: 100%;
      background-color: black;
    }
    #scrollbar {
      position: absolute;
      right: 0;
      top: 100px; /*there is a function that changes it to be correct*/
      height: 490px;
      -webkit-appearance: none;
      appearance: none;
      width: 20px;
      background-color: rgba(255,255,255,0.1);
      transform: scaleY(-1);
      writing-mode: bt-lr; /* IE */
      -webkit-appearance: slider-vertical; /* WebKit */
      appearance: slider-vertical; /* Standard */
    }
    #toolbar, #stack-selector {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 50px;
    background-color: #d0d0d0;
    overflow-x: auto;
    white-space: nowrap;
    }
    #toolbar img, #stack-selector button {
      cursor: pointer;
      margin: 0 10px;
      padding: 8px 10px;
      border: none;
    }
    #toolbar img {
      height: 20px;
    }
    #toolbar img:not(#resetBtn):not(#fullsizeBtn).active, #stack-selector button.active {
      background-color: #2a99ff;
      border: none;
    }
    #magnifier {
      position: absolute;
      display: none;
      width: 140px;
      height: 140px;
      border: 1px solid black;
      pointer-events: none;
    }
    #tooltip {
      display: none;
      position: absolute;
      background-color: rgba(50, 50, 50, 0.8);
      color: white;
      padding: 4px 8px;
      font-size: 14px;
      white-space: nowrap;
      border-radius: 4px;
    }
  </style>
</head>
<body>
  <div id="container">
    <div id="stack-selector"></div>
    <div id="toolbar">
      <img id="brightnessContrastBtn" src="icons/contrast-icon.png" alt="Brightness/Contrast">
      <img id="zoomBtn" src="icons/zoom-icon.png" alt="Zoom">
      <img id="panBtn" src="icons/pan-icon.png" alt="Pan">
      <img id="magnifyBtn" src="icons/magnify-icon.png" alt="Magnify">
      <img id="resetBtn" src="icons/reset-icon.png" alt="Reset">
      <img id="fullsizeBtn" src="icons/fullsize-icon.png" alt="Fullsize">
    </div>
    <canvas id="image" width="500" height="500"></canvas>
    <input id="scrollbar" type="range" min="1" max="10" value="1" orient="vertical">
    <canvas id="magnifier" width="140" height="140"></canvas>
  </div>
  <div id="tooltip"></div>
  <script>
    const stacks = {
      'Sagittal': [
        'https://github.com/jeremy-steinberg/Radiology-Stack-Scroller/blob/main/Stack%20Scroller/Axial/MRI_AXIAL_0001.jpg',
        'https://github.com/jeremy-steinberg/Radiology-Stack-Scroller/blob/main/Stack%20Scroller/Axial/MRI_AXIAL_0002.jpg',
      ],
      'Axial': [
        'https://github.com/jeremy-steinberg/Radiology-Stack-Scroller/blob/main/Stack%20Scroller/Axial/MRI_AXIAL_0001.jpg',
        'https://github.com/jeremy-steinberg/Radiology-Stack-Scroller/blob/main/Stack%20Scroller/Axial/MRI_AXIAL_0002.jpg',
      ]
    };
    let currentStack = Object.keys(stacks)[0];
    let imageFiles = stacks[currentStack];
    const image = document.getElementById('image');
    const ctx = image.getContext('2d', { willReadFrequently: true });
    const scrollbar = document.getElementById('scrollbar');
    const magnifier = document.getElementById('magnifier');
    const magCtx = magnifier.getContext('2d');
    let isThrottled = false;
    const brightnessContrastBtn = document.getElementById('brightnessContrastBtn');
    brightnessContrastBtn.addEventListener('click', () => {
    onButtonClick(brightnessContrastBtn);
    });
    const zoomBtn = document.getElementById('zoomBtn');
    zoomBtn.addEventListener('click', () => {
      onButtonClick(zoomBtn);
    });
    const panBtn = document.getElementById('panBtn');
    panBtn.addEventListener('click', () => {
      onButtonClick(panBtn);
    });
    const magnifyBtn = document.getElementById('magnifyBtn');
    magnifyBtn.addEventListener('click', () => {
      onButtonClick(magnifyBtn);
    });
    const resetBtn = document.getElementById('resetBtn');
    resetBtn.addEventListener('click', () => {
      onButtonClick(resetBtn);
    });
    const fullsizeBtn = document.getElementById('fullsizeBtn');
    fullsizeBtn.addEventListener('click', () => {
      onButtonClick(fullsizeBtn);
    });
    const stackSelector = document.getElementById('stack-selector');
    Object.keys(stacks).forEach((stackName) => {
      const stackButton = document.createElement('button');
      stackButton.textContent = stackName;
      stackButton.dataset.stack = stackName; // Add this line to set the data-stack attribute
      stackButton.addEventListener('click', () => {
        onStackButtonClick(stackName);
      });
      stackSelector.appendChild(stackButton);
    });
    let contrast = 100;
    let brightness = 100;
    let zoom = 1;
    let panOffset = { x: 0, y: 0 };
    let mouseDown = false;
    let start = { x: null, y: null };
    let activeFunction = 'brightnessContrast';
    function onButtonClick(button) {
        // Remove the "active" class from all buttons
        const buttons = document.querySelectorAll('#toolbar img');
        buttons.forEach((btn) => {
          btn.classList.remove('active');
        });
        // Add the "active" class to the clicked button
        button.classList.add('active');
      }
      function onStackButtonClick(stackName) {
      // Remove the "active" class from all stack buttons
      const stackButtons = document.querySelectorAll('#stack-selector button');
      stackButtons.forEach((btn) => {
        btn.classList.remove('active');
      });
      // Add the "active" class to the clicked stack button
      const clickedButton = document.querySelector(`#stack-selector button[data-stack="${stackName}"]`);
      clickedButton.classList.add('active');
      // Update the current stack and image files
      currentStack = stackName;
      imageFiles = stacks[currentStack];
      // Update scrollbar and display the first image of the new stack
      scrollbar.max = imageFiles.length;
      scrollbar.value = 1;
     
      updateImage(0);
      onResetBtnClick();
    }
   
    // Initialize with the first stack
    onStackButtonClick(currentStack);
    function applyFilters(ctx) {
      const imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
      const data = imageData.data;
      for (let i = 0; i < data.length; i += 4) {
        data[i] = Math.min(Math.max((data[i] - 128) * contrast / 100 + 128, 0) * brightness / 100, 255);
        data[i + 1] = Math.min(Math.max((data[i + 1] - 128) * contrast / 100 + 128, 0) * brightness / 100, 255);
        data[i + 2] = Math.min(Math.max((data[i + 2] - 128) * contrast / 100 + 128, 0) * brightness / 100, 255);
      }
      ctx.putImageData(imageData, 0, 0);
    }
    function updateImage(index) {
  if (index >= 0 && index < imageFiles.length) {
    const img = new Image();
    img.src = imageFiles[index];
    img.onload = () => {
      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
      ctx.save();
      const scaledWidth = ctx.canvas.width / zoom;
      const scaledHeight = ctx.canvas.height / zoom;
      const offsetX = (ctx.canvas.width - scaledWidth) / 2 + panOffset.x;
      const offsetY = (ctx.canvas.height - scaledHeight) / 2 + panOffset.y;
      ctx.drawImage(img, offsetX, offsetY, scaledWidth, scaledHeight);
      ctx.restore();
      drawIndicator(ctx, index, imageFiles.length);
      applyFilters(ctx);
    };
    scrollbar.value = imageFiles.length - index;
  }
}
    function drawMagnifier(ctx, mouseX, mouseY) {
      const magnifierSize = 50;
      const magnification = 2;
      const halfSize = magnifierSize / 2;
      const left = mouseX - halfSize;
      const top = mouseY - halfSize;
      const right = mouseX + halfSize;
      const bottom = mouseY + halfSize;
      ctx.save();
      ctx.beginPath();
      ctx.rect(left, top, magnifierSize, magnifierSize);
      ctx.clip();
      ctx.translate(mouseX - halfSize / magnification, mouseY - halfSize / magnification);
      ctx.scale(magnification, magnification);
      updateImage(imageFiles.length - parseInt(scrollbar.value));
      ctx.restore();
      ctx.strokeStyle = 'black';
      ctx.lineWidth = 2;
      ctx.strokeRect(left, top, magnifierSize, magnifierSize);
    }
    function drawIndicator(ctx, index, total) {
      ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
      ctx.fillRect(0, ctx.canvas.height - 30, 150, 30);
      ctx.font = '18px Arial';
      ctx.fillStyle = 'black';
      ctx.fillText(`Image ${index + 1} of ${total}`, 10, ctx.canvas.height - 10);
    }
    function onWheel(event) {
        event.preventDefault();
        const index = imageFiles.length - scrollbar.value;
        const newIndex = event.deltaY > 0 ? index + 1 : index - 1;
        updateImage(newIndex);
    }
    image.addEventListener('wheel', onWheel);
    function onMouseDown(event) {
      mouseDown = true;
      start.x = event.clientX;
      start.y = event.clientY;
    }
    function onMouseMove(event) {
    if (mouseDown) {
      const deltaX = start.x - event.clientX;
      const deltaY = start.y - event.clientY;
      if (activeFunction === 'brightnessContrast') {
        const newContrast = contrast + deltaY / 2;
        const newBrightness = brightness - deltaX / 2;
        updateContrast(Math.min(Math.max(newContrast, 0), 200));
        updateBrightness(Math.min(Math.max(newBrightness, 0), 200));
      } else if (activeFunction === 'zoom') {
        let newZoom = zoom - deltaY / 100;
        newZoom = Math.min(Math.max(newZoom, 0.2), 5);
        updateZoom(newZoom);
        start.y = event.clientY;
      } else if (activeFunction === 'pan') {
        panOffset.x -= deltaX;
        panOffset.y -= deltaY;
        updateImage(imageFiles.length - parseInt(scrollbar.value));
      } else if (activeFunction === 'magnify') {
          if (isThrottled) return;
          isThrottled = true;
          requestAnimationFrame(() => {
            isThrottled = false;
            const tempCanvas = document.createElement('canvas');
            tempCanvas.width = 70;
            tempCanvas.height = 70;
            const tempCtx = tempCanvas.getContext('2d');
            tempCtx.drawImage(image, event.clientX - 35, event.clientY - 35 - 100, 70, 70, 0, 0, 70, 70);
            const magX = event.clientX - magnifier.width / 2;
            const magY = event.clientY - magnifier.height / 2;
            magnifier.style.left = `${magX}px`;
            magnifier.style.top = `${magY}px`;
            magCtx.clearRect(0, 0, magnifier.width, magnifier.height);
            magnifier.style.display = 'block';
            magCtx.drawImage(tempCanvas, 0, 0, 70, 70, 0, 0, magnifier.width, magnifier.height);
          });
        }
      start.x = event.clientX;
      start.y = event.clientY;
      }
    }
    function openFullSize() {
      window.open(imageFiles[imageFiles.length - parseInt(scrollbar.value)], '_blank');
    }
    function onMouseUp() {
  mouseDown = false;
  if (activeFunction === 'magnify') {
    if (magnifyBtn.classList.contains('active')) {
      magnifier.style.display = 'none';
    } else {
      activeFunction = null;
    }
  }
}
    function onDragStart(event) {
      event.preventDefault();
    }
    function onScrollbarChange() {
      const index = imageFiles.length - parseInt(scrollbar.value);
      updateImage(index);
    }
    function onBrightnessContrastBtnClick() {
    activeFunction = 'brightnessContrast';
    image.style.cursor = 'cursor';
    }
    function onZoomBtnClick() {
    activeFunction = 'zoom';
    image.style.cursor = 'zoom-in';
    }
    function onPanBtnClick() {
      activeFunction = 'pan';
      image.style.cursor = 'grab';
    }
    function onMagnifyBtnClick() {
      activeFunction = 'magnify';
      image.style.cursor = 'zoom-in';
    }
    function onResetBtnClick() {
      contrast = 100;
      brightness = 100;
      zoom = 1;
      panOffset = { x: 0, y: 0 };
      focalPoint = { x: null, y: null };
      updateImage(imageFiles.length - parseInt(scrollbar.value));
      image.style.cursor = 'default';
      activeFunction = 'brightnessContrast';
    }
    function onFullsizeBtnClick() {
      activeFunction = 'Fullsize';
      image.style.cursor = 'default';
    }
    image.addEventListener('mousedown', onMouseDown);
    image.addEventListener('mousemove', onMouseMove);
    image.addEventListener('mouseup', onMouseUp);
    image.addEventListener('mouseleave', onMouseUp);
    image.addEventListener('dragstart', onDragStart);
    resetBtn.addEventListener('click', onResetBtnClick);
    scrollbar.addEventListener('input', onScrollbarChange);
    brightnessContrastBtn.addEventListener('click', onBrightnessContrastBtnClick);
    zoomBtn.addEventListener('click', onZoomBtnClick);
    panBtn.addEventListener('click', onPanBtnClick);
    magnifyBtn.addEventListener('click', onMagnifyBtnClick);
    fullsizeBtn.addEventListener('click', openFullSize);
    function updateContrast(value) {
      contrast = value;
      updateImage(imageFiles.length - parseInt(scrollbar.value));
    }
    function updateBrightness(value) {
      brightness = value;
      updateImage(imageFiles.length - parseInt(scrollbar.value));
    }
    function updateZoom(value) {
    zoom = value;
    updateImage(imageFiles.length - parseInt(scrollbar.value));
    }
   
    function updateScrollbarPosition() {
      const toolbar = document.getElementById('toolbar');
      const stackSelector = document.getElementById('stack-selector');
      const scrollbar = document.getElementById('scrollbar');
      const toolbarHeight = toolbar.offsetHeight;
      const stackSelectorHeight = stackSelector.offsetHeight;
      const scrollbarTopValue = toolbarHeight + stackSelectorHeight;
      scrollbar.style.top = `${scrollbarTopValue}px`;
    }
    function updateScrollbarHeight() {
      const imgHeight = document.getElementById('image').clientHeight;
      const toolbarHeight = document.getElementById('toolbar').offsetHeight;
      const stackSelectorHeight = document.getElementById('stack-selector').offsetHeight;
      const scrollbar = document.getElementById('scrollbar');
      scrollbar.style.height = `${imgHeight - 10}px`;
    }
    window.addEventListener('load', () => {
      updateScrollbarHeight();
      updateScrollbarPosition();
    });
    window.addEventListener('resize', () => {
      updateScrollbarHeight();
      updateScrollbarPosition();
    });
    const tooltip = document.getElementById('tooltip');
    function showTooltip(button) {
      if (stackSelector.contains(button)) {
        return;
      }
     
      tooltip.style.display = 'block';
      tooltip.innerText = button.alt;
      const buttonRect = button.getBoundingClientRect();
      const tooltipWidth = tooltip.offsetWidth;
      const tooltipHeight = tooltip.offsetHeight;
      tooltip.style.left = `${buttonRect.left + (buttonRect.width / 2) - (tooltipWidth / 2)}px`;
      tooltip.style.top = `${buttonRect.top + buttonRect.height + 10}px`; // Updated this line
    }
    function hideTooltip() {
      tooltip.style.display = 'none';
    }
    const buttons = document.querySelectorAll('#toolbar img, #stack-selector button');
    buttons.forEach((btn) => {
      btn.addEventListener('mouseenter', () => showTooltip(btn));
      btn.addEventListener('mouseleave', hideTooltip);
    });
    // Add these lines after the existing event listeners
    image.addEventListener('touchstart', (e) => {
      e.preventDefault();
      onMouseDown(e.touches[0]);
    });
    image.addEventListener('touchmove', (e) => {
      e.preventDefault();
      onMouseMove(e.touches[0]);
    });
    image.addEventListener('touchend', (e) => {
      e.preventDefault();
      onMouseUp();
    });
    image.addEventListener('touchcancel', (e) => {
      e.preventDefault();
      onMouseUp();
    });
    // Initialize with the first image
    updateImage(0);
  </script>
</body>
</html>
<html>
<html>
<!--  
<!--  

Latest revision as of 21:22, 30 March 2023

Image Viewer

Brightness/Contrast Zoom Pan Magnify Reset Fullsize


6A. Märzen

Overall Impression: An elegant, malty German amber lager with a clean, rich, toasty and bready malt flavor, restrained bitterness, and a dry finish that encourages another drink. The overall malt impression is soft, elegant, and complex, with a rich aftertaste that is never cloying or heavy.

History: As the name suggests, brewed as a stronger “March beer” in March and lagered in cold caves over the summer. Modern versions trace back to the lager developed by Spaten in 1841, contemporaneous to the development of Vienna lager. However, the Märzen name is much older than 1841; the early ones were dark brown, and in Austria the name implied a strength band (14 °P) rather than a style. The German amber lager version (in the Viennese style of the time) was first served at Oktoberfest in 1872, a tradition that lasted until 1990 when the golden Festbier was adopted as the standard festival beer.

6B. Rauchbier

Overall Impression: An elegant, malty German amber lager with a balanced, complementary beechwood smoke character. Toasty-rich malt in aroma and flavor, restrained bitterness, low to high smoke flavor, clean fermentation profile, and an attenuated finish are characteristic.

History: A historical specialty of the city of Bamberg, in the Franconian region of Bavaria in Germany. Beechwood-smoked malt is used to make a Märzen-style amber lager. The smoke character of the malt varies by maltster; some breweries produce their own smoked malt (rauchmalz).

6C. Dunkles Bock

Overall Impression: A dark, strong, malty German lager beer that emphasizes the malty-rich and somewhat toasty qualities of continental malts without being sweet in the finish.

History: Originated in the Northern German city of Einbeck, which was a brewing center and popular exporter in the days of the Hanseatic League (14th to 17th century). Recreated in Munich starting in the 17th century. The name “bock” is based on a corruption of the name “Einbeck” in the Bavarian dialect, and was thus only used after the beer came to Munich. “Bock” also means “Ram” in German, and is often used in logos and advertisements.

Source: BJCP Style Guidelines