Simulating the Fourier Series

I've created a unique drawing app that transforms sketches into Fourier series, but it's exhibiting an unusual behavior where it spirals inwards unexpectedly. Strangely, all the constants within the series are of similar size, which I suspect is incorrect, though I may be mistaken. Below is the code snippet:

var states = ["START", "DRAWING", "CIRCLES"];
var currentState = states[0];

var graph = [];
var constants = [];

// Half because ranging from -50 to 50
var halfNumCircles = 50;
var time = 0;
var deltaTime = 0.01;


// INITIAL SETUP
function setup() {
  createCanvas(window.innerWidth, window.innerHeight);
  angleMode(DEGREES);

  frameRate(30);
  cursor(CROSS);
}


// DRAWING LOOP
function draw() {
  background(255);

  // Axes
  stroke(100);
  line(width / 2, 0, width / 2, height);
  line(0, height / 2, width, height / 2);


  // Drawing
  stroke(0);
  if (currentState == states[1]) {
    // Add mousepos to graph
    graph.push([mouseX - width / 2, mouseY - height / 2]);

    // Draw graph
    for (let i = 0; i < graph.length - 1; i++) {
      line(graph[i][0] + width / 2, graph[i][1] + height / 2,
        graph[i + 1][0] + width / 2, graph[i + 1][1] + height / 2);
    }
  }


  // Circles
  stroke(0);
  if (currentState == states[2]) {
    // Starting at origin, draw lines to each boundary between circles
    var points = [[0, 0]];

    // For each constant, add a point
    for (let i = 0; i < 2 * halfNumCircles + 1; i++) {
      // n is 0,1,-1,2,-2...
      var n = 0;
      if (i % 2 == 0) {
        n = -i / 2;
      } else {
        n = i / 2;
      }

      var pointX = constants[i][0] * cos(n * 2 * Math.PI * time) -
        constants[i][1] * sin(n * 2 * Math.PI * time);
      var pointY = constants[i][0] * sin(n * 2 * Math.PI * time) +
        constants[i][1] * cos(n * 2 * Math.PI * time);

      // Add new arrow to the last one
      points.push([points[points.length - 1][0] + pointX, points[points.length - 1][1] + pointY]);
    }

    // Draw lines between points
    for (let i = 0; i < points.length - 1; i++) {
      line(points[i][0] + width / 2, points[i][1] + height / 2,
        points[i + 1][0] + width / 2, points[i + 1][1] + height / 2)
    }

    // Increment time
    time = (time + deltaTime);
  }
}


// FOURIER SERIES OF FUNCTION
function getConstants(graph) {
  // Returns array with constants
  // Note that constants are complex numbers

  // Set constants to 0, to be added to in the next loop
  var constants = []
  for (let i = 0; i < 2 * halfNumCircles + 1; i++) {
    constants.push([0, 0]);
  }

  // For each constant
  for (let c = -halfNumCircles; c <= halfNumCircles; c++) {
    var deltaT = 1.0 / graph.length;

    // Loop through the graph: sum of f(t)*e^{-c*2pi*i*t}*deltaT from 0 <= t <= 1
    for (let i = 0; i < graph.length; i++) {
      // Effective points on graph
      var a = graph[i][0];
      var b = graph[i][1];

      var t = i / graph.length;

      // Complex multiplication f(t)*e^{-c*2pi*i*t}
      var xChange = a * cos(-c * 2 * Math.PI * t) - b * sin(-c * 2 * Math.PI * t);
      var yChange = a * sin(-c * 2 * Math.PI * t) + b * cos(-c * 2 * Math.PI * t);

      constants[c + halfNumCircles][0] += xChange * deltaT;
      constants[c + halfNumCircles][1] += yChange * deltaT;
    }

  }

  // Reorder from [...-2, -1, 0, 1, 2...] to [0, 1, -1, 2, -2...]
  var orderedConstants = []
  for (let i = 0; i < 2 * halfNumCircles + 1; i++) {
    orderedConstants.push([0, 0]);
  }

  for (let i = 0; i < 2 * halfNumCircles + 1; i++) {
    if (i % 2 == 0) {
      orderedConstants[i] = constants[halfNumCircles - i / 2];
    } else {
      orderedConstants[i] = constants[halfNumCircles + (i + 1) / 2];
    }
  }

  return orderedConstants;
}


// STATE CHANGING EVENTS
function mousePressed() {
  // When clicked from start, start drawing
  // When clicked from circles, reset
  if (currentState == states[0]) {
    currentState = states[1];
  } else if (currentState == states[2]) {
    currentState = states[0];
    graph = [[]];
  }
}
function mouseReleased() {
  // When released, stop drawing, start circles
  if (currentState == states[1]) {
    currentState = states[2];
    time = 0;

    // Add first element of graph to the end, creating a loop
    graph.push(graph[0]);

    // Computationally intensive step
    constants = getConstants(graph);
  }
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Circle Drawing</title>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/p5.js" type="text/javascript"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/addons/p5.dom.js" type="text/javascript"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/addons/p5.sound.js" type="text/javascript"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/5.2.3/math.js" type="text/javascript"></script>

  <script src="circles.js" type="text/javascript"></script>

  <style media="screen">
    body {
      padding: 0;
      margin: 0;
    }
  </style>

</head>

<body>
</body>

</html>

The bug has left me puzzled. I considered whether it could be related to input scale, but since the units are arbitrary in this instance, that wouldn't make sense. If you have any insight into what might be amiss, please share. Thank you for your assistance!

Answer №1

The time unit for deltaTime is in milliseconds. However, the sample period is excessively large.

To convert the time to seconds, divide it by 1000.0:

var times_s = time/1000.0;
var pointX = constants[i][0] * cos(n * 2 * Math.PI * times_s) -
    constants[i][1] * sin(n * 2 * Math.PI * times_s);
var pointY = constants[i][0] * sin(n * 2 * Math.PI * times_s) +
    constants[i][1] * cos(n * 2 * Math.PI * times_s);

var states = ["START", "DRAWING", "CIRCLES"];
var currentState = states[0];

var graph = [];
var constants = [];

// Half because ranging from -50 to 50
var halfNumCircles = 50;
var time = 0;
var deltaTime = 0.01;


// SETUP INITIALIZATION
function setup() {
  createCanvas(window.innerWidth, window.innerHeight);
  angleMode(DEGREES);

  frameRate(30);
  cursor(CROSS);
}


// DRAWING LOOP
function draw() {
  background(255);

  // Axes
  stroke(100);
  line(width / 2, 0, width / 2, height);
  line(0, height / 2, width, height / 2);


  // Drawing
  stroke(0);
  if (currentState == states[1]) {
    // Append mouse position to the graph
    graph.push([mouseX - width / 2, mouseY - height / 2]);

    // Draw the graph
    for (let i = 0; i < graph.length - 1; i++) {
      line(graph[i][0] + width / 2, graph[i][1] + height / 2,
        graph[i + 1][0] + width / 2, graph[i + 1][1] + height / 2);
    }
  }


  // Circles
  stroke(0);
  if (currentState == states[2]) {
    // Start at origin and connect lines between circles
    var points = [[0, 0]];

    // Add a point for each constant
    for (let i = 0; i < 2 * halfNumCircles + 1; i++) {
      var n = 0;
      if (i % 2 == 0) {
        n = -i / 2;
      } else {
        n = i / 2;
      }

      var times_s = time/1000.0;
      var pointX = constants[i][0] * cos(n * 2 * Math.PI * times_s) -
        constants[i][1] * sin(n * 2 * Math.PI * times_s);
      var pointY = constants[i][0] * sin(n * 2 * Math.PI * times_s) +
        constants[i][1] * cos(n * 2 * Math.PI * times_s);

      
      points.push([points[points.length - 1][0] + pointX, points[points.length - 1][1] + pointY]);
    }

    // Connect points with lines
    for (let i = 0; i < points.length - 1; i++) {
      line(points[i][0] + width / 2, points[i][1] + height / 2,
        points[i + 1][0] + width / 2, points[i + 1][1] + height / 2)
    }

    // Increment time
    time = (time + deltaTime);
  }
}


// OBTAIN FOURIER SERIES OF THE FUNCTION
function getConstants(graph) {
  var constants = []
  for (let i = 0; i < 2 * halfNumCircles + 1; i++) {
    constants.push([0, 0]);
  }

  for (let c = -halfNumCircles; c <= halfNumCircles; c++) {
    var deltaT = 1.0 / graph.length;

    for (let i = 0; i < graph.length; i++) {
      var a = graph[i][0];
      var b = graph[i][1];

      var t = i / graph.length;

      var xChange = a * cos(-c * 2 * Math.PI * t) - b * sin(-c * 2 * Math.PI * t);
      var yChange = a * sin(-c * 2 * Math.PI * t) + b * cos(-c * 2 * Math.PI * t);

      constants[c + halfNumCircles][0] += xChange * deltaT;
      constants[c + halfNumCircles][1] += yChange * deltaT;
    }

  }

  
  var orderedConstants = []
  for (let i = 0; i < 2 * halfNumCircles + 1; i++) {
    orderedConstants.push([0, 0]);
  }

  for (let i = 0; i < 2 * halfNumCircles + 1; i++) {
    if (i % 2 == 0) {
      orderedConstants[i] = constants[halfNumCircles - i / 2];
    } else {
      orderedConstants[i] = constants[halfNumCircles + (i + 1) / 2];
    }
  }

  return orderedConstants;
}


// STATE TRANSITION EVENTS
function mousePressed() {
  if (currentState == states[0]) {
    currentState = states[1];
  } else if (currentState == states[2]) {
    currentState = states[0];
    graph = [[]];
  }
}
function mouseReleased() {
  if (currentState == states[1]) {
    currentState = states[2];
    time = 0;

    graph.push(graph[0]);

    constants = getConstants(graph);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.js"></script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Generating Unique IDs in PHP with AJAX Without Form Submission

When I fill up the Name, Phone, Course, and Batch fields in my form, I want the Roll Number field to automatically generate a value without submitting the form. However, even after filling up the first 4 fields, no value appears in the Roll Number field. B ...

Utilizing ReactJs to Generate a Random Number for Visualization in a Material UI Progress Bar

I am looking to generate a random number for my test functionality in order to display it within a Material UI Progress bar. I have successfully implemented this piece of JavaScript code on JSFiddle, but now I want to integrate it into my React application ...

What are the best practices for creating documentation in ReactJS?

I need to generate documentation in a .doc file format for each component defined in our ReactJS application. I am seeking an npm package that can assist with this process by extracting code and comments from my components and converting them into docume ...

Combine the selected values of two dropdowns and display the result in an input field using Angular

I am working on a component that consists of 2 dropdowns. Below is the HTML code snippet for this component: <div class="form-group"> <label>{{l("RoomType")}}</label> <p-dropdown [disabled] = "!roomTypes.length" [options]= ...

Using object map filtering instead of array filtering in AngularJS

If I have a controller with a $scope property that is an object with other properties rather than an array, how can I properly filter the ng-repeat set in AngularJS? Check out this JSFiddle example: http://jsfiddle.net/ZfGx4/110/ This is how the controll ...

What is the best way to isolate particular components of an argument and store them in separate variables?

Currently, I am facing a challenge in extracting the name and id of an emoji from a discord argument using discord.js. The input provided to me is <:hack_wump:670702611627769876>, and my goal is to retrieve var id = '670702611627769876' alo ...

What is the best method for testing different versions of the same module simultaneously?

My goal is to distribute a module across various component manager systems like npmjs and bower. I also want to provide downloadable builds in different styles such as AMD for requirejs, commonJS, and a global namespace version for browsers - all minified. ...

The Angular directive is failing to refresh the data on the Google Map

I created a directive called "myMap" to incorporate a Google map into my application. However, I am facing an issue when attempting to update the longitude and latitude values for a different location using a controller function. The directive does not ref ...

Output is not being displayed by Ajax

I created a basic PHP code that populates rows and columns with an asterisk (*). To test the PHP code, I entered the URL localhost/squareService.php?rows=3&cols=3 However, when I asked a user to input the number of rows and columns using HTML and Java ...

React Redux - There is an error during rendering as expected props have not been received yet

After retrieving data from an API and storing it in the Redux state, I utilize a helper function within mapStateToProps to filter and modify a portion of that data before passing it along as props. Although everything appears to be functioning correctly b ...

Is there a way I can utilize canvas to overlay one image onto another?

Is there a way to merge one image into another by using a transparent box? It's not just about copying and pasting an image into another. I also want to know how to transform the image to fit perfectly within the other image. For example, similar to ...

An issue arises when trying to group and sum an array of objects due to difficulty converting strings to arrays in TypeScript

Below is the provided code snippet: Definition of Interface - interface IWEXInterface { readonly Date?: string; "Exec Qty"?: string; readonly Expiry?: string; } Data Collection - let data: IWEXInterface[] = [ { Date: &qu ...

How do I initiate a custom button click event in React from an event handler in another component?

I have a unique React application that utilizes the Material UI framework for its user interface design. Specifically, I have developed a specialized button component that customizes the default Material UI button and integrates with Redux. Within the ren ...

Enhanced efficiency in the interaction between front-end JavaScript and back-end JavaScript

As a frontend developer, I find myself in the unique position of working on a project that involves processing a large amount of data in my nodeJS backend (while using reactJS for the front end). After the necessary data processing is completed in the bac ...

Despite mutating the state, the Redux reducer does not trigger a rerender on my React Component

Lately, I've been facing challenges with redux as it sometimes doesn't trigger the rerendering of my React components. I understand that I need to update the state in order for Redux to detect changes. However, even after doing so, my React Compo ...

Does JavaScript Map Access Use Indexing for Efficient map.get Retrieval?

Is the map.get method in V8 optimized by indexing JavaScript-Map-object's keys, or does it loop through the entire map to find a key match? I'm curious about the efficiency of map.get with larger mappings containing 500,000+ key/value pairs. I h ...

What is the best way to trigger the ajax request with the same post parameter upon pressing the browser's back button?

Is there a way to remember the post parameters and resend an AJAX request when the browser back button is pressed? I have searched online and found a couple of methods: Using localsotrage or document.location.hash when the page unloads. Using cookie ...

Manipulating CSS Class Properties Using JavaScript

In my JavaScript application, there is a functionality that loads a list of items for users to click and view detailed information in a separate div on the page. Users should be able to interact with and make modifications to these individual item overview ...

Step by step guide on showcasing live server information obtained from the user-end through AJAX technique in a Javascript Pie Chart, within an ASP.NET html template

I have successfully managed to transfer data from the Client Side (C# Back End) to the Server Side (Javascript HTML in aspx) using AJAX. The example I found online demonstrated data display inside a div, but I'm unsure how to dynamically display my ow ...

Having trouble loading a JavaScript file in Nuxt? Experiencing some unexpected behavior?

I have a template with HTML/CSS/JS files that I want to make dynamic using Nuxt.js. I started by copying the index.html to the Nuxt project and transferring all the necessary data to nuxt.config.js, including CSS and JS files. The page renders without erro ...