<commit_before>import React from 'react';
import styles from './index.styl';
import range from 'lodash.range';
import classNames from 'classnames';
import animatedComponent from 'mixins/animatedComponent';

const FOLLOWER_STARS = 20;
const MAX_CONCURRENT_STARS = FOLLOWER_STARS + 50;

const GRAVITY = 0.4;
const STRIPE_WIDTH = 100;
const STAR_RADIUS = 32 / 2;
const height = window.innerHeight;

function updateFollowerStar(delta, star) {
  star.y += star.vy * delta;

  if(Math.abs(star.y) > 10) {
    star.y = Math.sign(star.y) * 10;
    star.vy *= -1;
  }

  return star;
}

function updateStar(delta, star) {
  if(star.isFollower) {
    return updateFollowerStar(delta, star);
  }

  star.x += star.vx * delta;
  star.y += star.vy * delta;
  star.vy += GRAVITY * delta;
  return star;
}

function isInViewport(progress, star) {
  return star.y + STAR_RADIUS + progress * height < height && (-star.x - STAR_RADIUS) < STRIPE_WIDTH;
}

export default React.createClass(animatedComponent({
  getInitialState() {
    return {
      stars: range(FOLLOWER_STARS).map((_, i) => ({
        x: i * -(STRIPE_WIDTH / FOLLOWER_STARS),
        y: -10 + 20 * Math.random(),
        vy: -8 + 16 * Math.random(),
        isFollower: true
      }))
    }
  },
  shouldComponentUpdate(props, state) {
    return state.stars !== this.state.stars;
  },
  update(delta, worldTick) {

    const running = this.props.progress < 1;
    const maxReached = this.state.stars.length === MAX_CONCURRENT_STARS;

    const canCreateStars = !maxReached && running;


    const newStars = canCreateStars ? 1 : 0;

    const stars = this.state.stars
    // Create new stars
    .concat(range(newStars).map(() => {
      return {
        x: -Math.random() * STRIPE_WIDTH,
        y: 0,
        vx: -2 + Math.random() * 4,
        vy: -7 + -5 * Math.random()
      };
    }))
    .filter((star) => isInViewport(this.props.progress, star))
    // Update all stars
    .map((star) => updateStar(delta, star));

    this.setState({stars});

    if(!running && stars.length === 0) {
      this.cancelAnimation();
    }
  },
  render() {
    return (
      <div className={classNames(styles.main, this.props.className)}>
        {
          this.state.stars.map((star) => {
            const style = {
              bottom: -star.y,
              left: -star.x
            };
            return <div className={styles.star} style={style}></div>;
          })
        }
      </div>
    );
  }
}))
<commit_msg>physics and performance tuning<commit_after>import React from 'react';
import styles from './index.styl';
import range from 'lodash.range';
import classNames from 'classnames';
import animatedComponent from 'mixins/animatedComponent';

const FOLLOWER_STARS = 25;
const MAX_CONCURRENT_STARS = 75 + FOLLOWER_STARS;

const GRAVITY = 1;
const STRIPE_WIDTH = 100;
const STAR_RADIUS = 32 / 2;
const STARS_PER_TICK = 1;
const height = window.innerHeight;

function updateFollowerStar(progress, delta, star) {
  star.y += star.vy * delta;
  star.x += star.vx * delta;

  if(progress === 1) {
    star.isFollower = false;
    return star;
  }

  if(Math.abs(star.y) > 10) {
    star.y = Math.sign(star.y) * 10;
    star.vy *= -1;
  }


  if(star.x > 0 || star.x < -STRIPE_WIDTH) {
    star.vx *= -1;
  }

  return star;
}

function updateStar(progress, delta, star) {
  if(star.isFollower) {
    return updateFollowerStar(progress, delta, star);
  }

  star.x += star.vx * delta;
  star.y += star.vy * delta;
  star.vy += GRAVITY * delta;

  if(progress < 1 && !isInViewport(progress, star)) {
    return {...star, ...createStar(star.id)};
  }

  return star;
}

function isInViewport(progress, star) {
  return star.y - (STAR_RADIUS * 2) + progress * height < height &&
    (-star.x - STAR_RADIUS) < STRIPE_WIDTH;
}

function createStar(id) {
  return {
    id,
    x: -Math.random() * STRIPE_WIDTH,
    y: 0,
    vx: -2 + Math.random() * 4,
    vy: -12 + -5 * Math.random()
  };
}

export default React.createClass(animatedComponent({
  getInitialState() {
    return {
      stars: range(FOLLOWER_STARS).map((_, i) => ({ ...createStar(i),
        x: i * -(STRIPE_WIDTH / FOLLOWER_STARS),
        y: -10 + 20 * Math.random(),
        vx: -1 + Math.random() * 2,
        vy: -8 + 16 * Math.random(),
        isFollower: true
      })),
      nextId: FOLLOWER_STARS + 1
    }
  },
  shouldComponentUpdate(props, state) {
    return state.stars !== this.state.stars;
  },
  update(delta, worldTick) {

    const progress = this.props.progress;
    const running = progress < 1;

    const maxStarsReached = this.state.stars.length >= MAX_CONCURRENT_STARS;

    const canCreateStars = !maxStarsReached && running;

    const newStars = !canCreateStars ? [] : range(STARS_PER_TICK)
      .map((i) => createStar(this.state.nextId + i));

    const stars = this.state.stars
    // Create new stars
    .concat(newStars)
    .map((star) => updateStar(progress, delta, star))
    .filter((star) => isInViewport(progress, star));

    this.setState({
      stars,
      nextId: this.state.nextId + newStars.length
    });

    if(!running && stars.length === 0) {
      this.cancelAnimation();
    }
  },
  render() {
    return (
      <div className={classNames(styles.main, this.props.className)}>
        {
          this.state.stars.map((star) => {
            const style = {
              bottom: -star.y,
              left: -star.x
            };
            return <div key={star.id} className={styles.star} style={style}></div>;
          })
        }
      </div>
    );
  }
}))

{'fraction_non_alphanumeric': 0.15112714156898108, 'fraction_numerical': 0.013165013525698828, 'mean_word_length': 2.552850736707239, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5805751', 'n_tokens_mistral': 2049, 'n_tokens_neox': 1893, 'n_words': 417}