Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                .container
  h3.header Modular Multiplication


.container
  .row
    #container.col-sm-6
    
    #side.col-sm-6
    
      h3 Options
      
      .row
        label(for="modulo").col-sm-4 Modulo
        input(type="number" id="modulo" step="1").col-sm-8.form-control
                
      .row
        label(for="coef").col-sm-4 Multiplier
        input(type="number" id="coef" step="0.1").col-sm-8.form-control
        
      .row
        .col-sm-12
          label.custom-control.custom-checkbox
            input(type="checkbox" id="autoAlpha").custom-control-input
            span.custom-control-indicator
            span.custom-control-description Automatically adjust alpha
        
      .row
        label(for="").col-sm-4 View
        .col-sm-8
          label.custom-control.custom-checkbox
            input(type="checkbox" id="drawCircle").custom-control-input
            span.custom-control-indicator
            span.custom-control-description Circle
          label.custom-control.custom-checkbox
            input(type="checkbox" id="drawPoints").custom-control-input
            span.custom-control-indicator
            span.custom-control-description Points
          label.custom-control.custom-checkbox
            input(type="checkbox" id="drawLines").custom-control-input
            span.custom-control-indicator
            span.custom-control-description Lines
          label.custom-control.custom-checkbox
            input(type="checkbox" id="drawText").custom-control-input
            span.custom-control-indicator
            span.custom-control-description Text
          label.custom-control.custom-checkbox
            input(type="checkbox" id="drawTitle").custom-control-input
            span.custom-control-indicator
            span.custom-control-description Title
      
      .row
        label(for="res").col-sm-4 Resolution
        input(type="number" id="res" step="20").col-sm-8.form-control
        
      .row
        label(for="export").col-sm-4 Save this!
        input(type="button" id="export" value="Export").btn.btn-primary.col-sm-4
        
      .row
        .col-sm-12
          .small More options if you can find them in the code! 😜
      
  
  h3 How does this work?
  .row
    .col-sm-6
      img(src="https://image.ibb.co/jwaoF6/01.png", alt="").img-fluid
    .col-sm-6
      p Draw ten points evenly spread around a circle.
  .row
    .col-sm-6
      img(src="https://image.ibb.co/cVYx8R/02.png", alt="").img-fluid
    .col-sm-6
      p Multiply each point by two. Take the remainder of the division of the result by ten, and draw a line between the two points.
  .row
    .col-sm-6
      img(src="https://image.ibb.co/iRFYhm/03.png", alt="").img-fluid
    .col-sm-6
      p Here is the same operation, but for 50 points.
  .row
    .col-sm-6
      img(src="https://image.ibb.co/dNFoF6/04.png", alt="").img-fluid
    .col-sm-6
      p Hiding the points and text, we see a "nice" shape emerge.
  .row
    .col-sm-6
      img(src="https://image.ibb.co/j9hqTR/05.png", alt="").img-fluid
    .col-sm-6
      p Try it out with higher or even negative multipliers!
      
  
  h3 Some examples...
  
  .row
    - var urls = ['https://image.ibb.co/fcjtnm/example0.png', 'https://image.ibb.co/ja9R7m/example1.png', 'https://image.ibb.co/geEtnm/example2.png', 'https://image.ibb.co/jWU4tR/example3.png', 'https://image.ibb.co/dzjF06/example4.png', 'https://image.ibb.co/cNh2f6/example5.png'];
    each url in urls
      .col-sm-4
        img(src=url, alt="").img-fluid
  
  
  h3 About  
  p Inspired by this 
    a(href="https://www.youtube.com/watch?v=-X49VQgi86E") video
  p Made with 💗 by 
    a(href="codepen.io/ninivert") ninivert
              
            
!

CSS

              
                html,
body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
}

body {
  $base-font-size: 18px;
  $heading-scale: 8;
  @for $i from 1 through 6 {
    h#{$i} {
      margin: .5em 0;
      font-size: $base-font-size + $heading-scale * (6 - $i);
      color: #2c3e50;
    }
  }
  color: #34495e;
  
  .header {
    margin-top: .4em;
    padding: .4em 0;
    position: relative;
    &::after {
      content: '';
      position: absolute;
      bottom: 0;
      left: 0;
      display: block;
      width: 2em;
      height: 2px;
      background: #2980b9;
    }
    // border-bottom: 2px solid #2980b9;
  }
  
  .row {
    > #side {
      > .row {
        margin-bottom: .5em;
      }
    }
  }
  
  #container {
    // overflow: auto;
    > canvas {
      width: 100% !important;
      height: auto !important;
    }
  }
}
              
            
!

JS

              
                function init() {
  window.c = document.createElement('canvas');
  c.width = c.height = 500;
  document.getElementById('container').appendChild(c);
  window.ctx = c.getContext('2d');
  ctx.translate(c.width/2, c.height/2); // center around the center of the circle
  
  // Options
  window.opts = {
    modulo: 1000, // modulo
    coef: 2, // multiplier
    autoAlpha: true, // adjust the alpha depending on the number of points
    drawCircle: true,
    drawPoints: false,
    drawLines: true,
    drawText: false,
    drawTitle: true,
    res: Math.min(c.width, c.height), // resolution
    export: null, // export button placeholder
    ENDOFOPTIONS: null, // Placeholder
    radius: Math.min(c.width/2, c.height/2)-80, // radius of the big circle
    pointRadius: 4, // radius of the points
    circleColor: '#cccccc',
    pointColor: '#16a085',
    lineColor: '#222222',
    textColor: '#1abc9c',
    titleColor: '#2c3e50',
    bgColor: '#ffffff',
    elmts: {},
    handlers: {
      modulo: function() {
        let val = Math.max(this.value, 1);
        opts.modulo = val;
        this.value = val;
        draw();
      },
      coef: function() {
        let val = this.value;
        opts.coef = val;
        this.value = val;
        draw();
      },
      autoAlpha: function() {
        opts.autoAlpha = this.checked;
        draw();
      },
      drawCircle: function() {
        opts.drawCircle = this.checked;
        draw();
      },
      drawPoints: function() {
        opts.drawPoints = this.checked;
        draw();
      },
      drawLines: function() {
        opts.drawLines = this.checked;
        draw();
      },
      drawText: function() {
        opts.drawText = this.checked;
        draw();
      },
      drawTitle: function() {
        opts.drawTitle = this.checked;
        draw();
      },
      res: function() {
        // Retranslate the canvas back to (0, 0)
        ctx.translate(-c.width/2, -c.height/2);
        // Adjust the variables that depend on resolution
        let val = Math.max(this.value, 200);
        c.width = c.height = val;
        opts.res = val;
        opts.radius = val/2-80;
        // Apply new translation
        ctx.translate(val/2, val/2);
        // Update and render
        this.value = val;
        draw();
      },
      export: function() {
        let link = document.createElement('a');
        link.download = `Modular multiplication of ${opts.coef} modulo ${opts.modulo}`;
        link.href = c.toDataURL('image/png');
        // Need to add the link to the body for Firefox
        document.body.appendChild(link);
        link.setAttribute("type", "hidden");
        link.click();
      }
    },
    init: function() {
      let keys = Object.keys(this),
          elmt, type;

      for (let i of keys) {
        // Stop when you arrive at the end of the option list
        if (i === 'ENDOFOPTIONS') break;

        this.elmts[i] = document.getElementById(i);
        
        elmt = this.elmts[i];
        type = elmt.type;

        if (type === 'checkbox' || type === 'button') {
          elmt.checked = this[i];
          elmt.onclick = this.handlers[i];
        }
        if (type === 'number') {
          elmt.value = this[i];
          elmt.onchange = this.handlers[i];
        }
      }
    }
  };
  
  opts.init();
  
  draw();
}


function draw() {
  /**
   * Utility functions
   * Get the index of the point to bind to
   * Get an angle from a point relative to the center of the circle
     -Math.PI/2 to have point 0 facing up
   * Get the of a point from said angle and radius
   */
  let getBindingPoint = n => n*opts.coef%opts.modulo;
  let getAngle = ratio => ratio*2*Math.PI-Math.PI/2;
  let getCoords = (a, r) => ({x: Math.cos(a)*r, y: Math.sin(a)*r});
  
  // Clear the canvas
  ctx.globalAlpha = 1;
  ctx.fillStyle = opts.bgColor;
  ctx.fillRect(-c.width/2, -c.height/2, c.width, c.height);
  
  // Draw external circle
  if (opts.drawCircle) {
    ctx.beginPath();
    ctx.arc(0, 0, opts.radius, 2*Math.PI, 0);
    ctx.strokeStyle = opts.circleColor;
    ctx.stroke();
  }
  
  // Draw title
  if (opts.drawTitle) {
    ctx.textAlign = 'left';
    ctx.textBaseline = 'top';
    ctx.font = 'italic 16pt Arial';
    ctx.fillStyle = opts.titleColor;
    ctx.fillText(`Modular multiplication of ${opts.coef} modulo ${opts.modulo}`, -c.width/2+20, -c.height/2+20, c.width-40);
  }
  
  let point, bindingPoint, textPoint;

  // Draw points, lines and text
  for (let i=0; i<opts.modulo; i++) {
    
    // Draw the point
    point = getCoords( getAngle(i/opts.modulo), opts.radius );
    if (opts.drawPoints) {
      ctx.beginPath();
      ctx.arc(point.x, point.y, opts.pointRadius, 2*Math.PI, 0);
      ctx.fillStyle = opts.pointColor;
      ctx.fill();
    }
  
    // Draw the line
    if (opts.drawLines) {
      bindingPoint = getCoords( getAngle(getBindingPoint(i)/opts.modulo), opts.radius );
      if (opts.autoAlpha) ctx.globalAlpha = Math.min(opts.res*.2/opts.modulo, 1);
      ctx.beginPath();
      ctx.moveTo(point.x, point.y);
      ctx.lineTo(bindingPoint.x, bindingPoint.y);
      ctx.strokeStyle = opts.lineColor;
      ctx.stroke();
      ctx.globalAlpha = 1;
    }
  
    // Draw the text
    if (opts.drawText) {
      textPoint = getCoords( getAngle(i/opts.modulo), opts.radius*1.08 );
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.font = '12pt Arial'
      ctx.fillStyle = opts.textColor;
      ctx.fillText(i, textPoint.x, textPoint.y);
    }
  }
}


init();
              
            
!
999px

Console