<commit_before>package net.kopeph.ld31.graphics;

import java.util.Arrays;

import net.kopeph.ld31.LD31;
import net.kopeph.ld31.Level;
import net.kopeph.ld31.entity.Enemy;
import net.kopeph.ld31.util.ThreadPool;
import net.kopeph.ld31.util.Util;
import processing.core.PApplet;
import processing.core.PImage;

/** @author stuntddude */
public class Renderer {
	public PImage textureRed    , rawTextureRed;
	public PImage textureGreen  , rawTextureGreen;
	public PImage textureBlue   , rawTextureBlue;
	public PImage textureCyan   , rawTextureCyan;
	public PImage textureMagenta, rawTextureMagenta;
	public PImage textureYellow , rawTextureYellow;
	public PImage textureGrey   , rawTextureGrey;
	public PImage textureWhite  , rawTextureWhite;
	public PImage textureBlack  , rawTextureBlack;
	public Font font;

	public int viewX = 0, viewY = 0;

	private final LD31 context;
	private final ThreadPool renderingPool = new ThreadPool();

	public Renderer() {
		context = LD31.getContext();

		//load raw textures
		rawTextureRed     = context.loadImage("res/textures/red-background.jpg"    ); //$NON-NLS-1$
		rawTextureGreen   = context.loadImage("res/textures/green-background.jpg"  ); //$NON-NLS-1$
		rawTextureBlue    = context.loadImage("res/textures/blue-background.jpg"   ); //$NON-NLS-1$
		rawTextureCyan    = context.loadImage("res/textures/cyan-background.jpg"   ); //$NON-NLS-1$
		rawTextureMagenta = context.loadImage("res/textures/magenta-background.jpg"); //$NON-NLS-1$
		rawTextureYellow  = context.loadImage("res/textures/yellow-background.jpg" ); //$NON-NLS-1$
		rawTextureGrey    = context.loadImage("res/textures/grey-background.jpg"   ); //$NON-NLS-1$
		rawTextureWhite   = context.loadImage("res/textures/white-background.jpg"  ); //$NON-NLS-1$
		rawTextureBlack   = context.loadImage("res/textures/black-background.jpg"  ); //$NON-NLS-1$

		font = new Font("res/font-16-white.png"); //$NON-NLS-1$
	}

	public void cropTextures(int width, int height) {
		textureRed     = Util.crop(rawTextureRed    , width, height);
		textureGreen   = Util.crop(rawTextureGreen  , width, height);
		textureBlue    = Util.crop(rawTextureBlue   , width, height);
		textureCyan    = Util.crop(rawTextureCyan   , width, height);
		textureMagenta = Util.crop(rawTextureMagenta, width, height);
		textureYellow  = Util.crop(rawTextureYellow , width, height);
		textureGrey    = Util.crop(rawTextureGrey   , width, height);
		textureWhite   = Util.crop(rawTextureWhite  , width, height);
		textureBlack   = Util.crop(rawTextureBlack  , width, height);
	}

	public void calculateLighting(int[] lighting, Level level) {
		viewX = level.player.x() - context.lastWidth/2;
		viewY = level.player.y() - context.lastHeight/2;

		Arrays.fill(lighting, Level.FLOOR_NONE);

		//crop the tiles array into the pixels array
		//assumes the bottom left of the screen won't be outside the level unless the level is smaller than the screen
		int sourceX = PApplet.max(viewX, 0);
		int sourceY = PApplet.max(viewY, 0);

		int destinationX = PApplet.max(-viewX, 0);
		int destinationY = PApplet.max(-viewY, 0);

		int cropWidth = PApplet.min(level.LEVEL_WIDTH - sourceX, context.lastWidth - destinationX);
		int cropHeight = PApplet.min(level.LEVEL_HEIGHT - sourceY, context.lastHeight - destinationY);

		for (int y = destinationY; y < destinationY + cropHeight; ++y) {
			System.arraycopy(level.tiles, (y + viewY)*level.LEVEL_WIDTH + sourceX, lighting, y*context.lastWidth + destinationX, cropWidth);
		}

		for (final Enemy e : level.enemies) {
			//only render enemies that have a chance of casting light into the scene
			if (e.screenX() > -e.viewDistance + 1 && e.screenX() < context.lastWidth + e.viewDistance - 2 &&
				e.screenY() > -e.viewDistance + 1 && e.screenY() < context.lastHeight + e.viewDistance - 2) {
				//TODO: distance formula to check for circle intersection with screen corners (minor optimization to ignore certain enemies)

				//create a new thread to run the lighting process of each enemy
				renderingPool.post(() -> { e.rayTrace(lighting, e.viewDistance, e.color); });
			}
		}

		renderingPool.forceSync();
	}

	public void applyTexture(final int[] pixels) {
		float taskSize = pixels.length/renderingPool.poolSize;
		for (int i = 0; i < renderingPool.poolSize; ++i) {
			final int j = i;
			renderingPool.post(() -> { applyTextureImpl(pixels, PApplet.round(j*taskSize), PApplet.round((j+1)*taskSize)); });
		}

		renderingPool.forceSync();
	}

	private void applyTextureImpl(final int[] pixels, int iBegin, int iEnd) {
		for (int i = iBegin; i < iEnd; ++i) {
			switch (pixels[i]) {
				case Level.FLOOR_NONE:    pixels[i] = textureBlack.pixels[i];   break;
				case Level.FLOOR_RED:     pixels[i] = textureRed.pixels[i];     break;
				case Level.FLOOR_GREEN:   pixels[i] = textureGreen.pixels[i];   break;<commit_msg>optimized a corner case (closes #21)<commit_after>package net.kopeph.ld31.graphics;

import java.util.Arrays;

import net.kopeph.ld31.LD31;
import net.kopeph.ld31.Level;
import net.kopeph.ld31.entity.Enemy;
import net.kopeph.ld31.util.ThreadPool;
import net.kopeph.ld31.util.Util;
import processing.core.PApplet;
import processing.core.PImage;

/** @author stuntddude */
public class Renderer {
	public PImage textureRed    , rawTextureRed;
	public PImage textureGreen  , rawTextureGreen;
	public PImage textureBlue   , rawTextureBlue;
	public PImage textureCyan   , rawTextureCyan;
	public PImage textureMagenta, rawTextureMagenta;
	public PImage textureYellow , rawTextureYellow;
	public PImage textureGrey   , rawTextureGrey;
	public PImage textureWhite  , rawTextureWhite;
	public PImage textureBlack  , rawTextureBlack;
	public Font font;

	public int viewX = 0, viewY = 0;

	private final LD31 context;
	private final ThreadPool renderingPool = new ThreadPool();

	public Renderer() {
		context = LD31.getContext();

		//load raw textures
		rawTextureRed     = context.loadImage("res/textures/red-background.jpg"    ); //$NON-NLS-1$
		rawTextureGreen   = context.loadImage("res/textures/green-background.jpg"  ); //$NON-NLS-1$
		rawTextureBlue    = context.loadImage("res/textures/blue-background.jpg"   ); //$NON-NLS-1$
		rawTextureCyan    = context.loadImage("res/textures/cyan-background.jpg"   ); //$NON-NLS-1$
		rawTextureMagenta = context.loadImage("res/textures/magenta-background.jpg"); //$NON-NLS-1$
		rawTextureYellow  = context.loadImage("res/textures/yellow-background.jpg" ); //$NON-NLS-1$
		rawTextureGrey    = context.loadImage("res/textures/grey-background.jpg"   ); //$NON-NLS-1$
		rawTextureWhite   = context.loadImage("res/textures/white-background.jpg"  ); //$NON-NLS-1$
		rawTextureBlack   = context.loadImage("res/textures/black-background.jpg"  ); //$NON-NLS-1$

		font = new Font("res/font-16-white.png"); //$NON-NLS-1$
	}

	public void cropTextures(int width, int height) {
		textureRed     = Util.crop(rawTextureRed    , width, height);
		textureGreen   = Util.crop(rawTextureGreen  , width, height);
		textureBlue    = Util.crop(rawTextureBlue   , width, height);
		textureCyan    = Util.crop(rawTextureCyan   , width, height);
		textureMagenta = Util.crop(rawTextureMagenta, width, height);
		textureYellow  = Util.crop(rawTextureYellow , width, height);
		textureGrey    = Util.crop(rawTextureGrey   , width, height);
		textureWhite   = Util.crop(rawTextureWhite  , width, height);
		textureBlack   = Util.crop(rawTextureBlack  , width, height);
	}

	public void calculateLighting(int[] lighting, Level level) {
		viewX = level.player.x() - context.lastWidth/2;
		viewY = level.player.y() - context.lastHeight/2;

		Arrays.fill(lighting, Level.FLOOR_NONE);

		//crop the tiles array into the pixels array
		int sourceX = PApplet.max(viewX, 0);
		int sourceY = PApplet.max(viewY, 0);

		int destinationX = PApplet.max(-viewX, 0);
		int destinationY = PApplet.max(-viewY, 0);

		int cropWidth = PApplet.min(level.LEVEL_WIDTH - sourceX, context.lastWidth - destinationX);
		int cropHeight = PApplet.min(level.LEVEL_HEIGHT - sourceY, context.lastHeight - destinationY);

		for (int y = destinationY; y < destinationY + cropHeight; ++y) {
			System.arraycopy(level.tiles, (y + viewY)*level.LEVEL_WIDTH + sourceX, lighting, y*context.lastWidth + destinationX, cropWidth);
		}

		for (final Enemy e : level.enemies) {
			final int x = e.screenX(), y = e.screenY(), vd = e.viewDistance, vdsq = vd*vd;
			final int w = context.lastWidth - 1, h = context.lastHeight - 1;

			//only render enemies that have a chance of casting light into the scene
			if (x > -e.viewDistance + 1 && x < context.lastWidth + e.viewDistance - 2 &&
				y > -e.viewDistance + 1 && y < context.lastHeight + e.viewDistance - 2) {

				//distance formula to check for circle intersection with screen corners (minor optimization to ignore certain enemies)
				if (x < 0 && y < 0 &&      x * x      +      y * y      >= vdsq) continue;
				if (x < 0 && y > h &&      x * x      + (y - h)*(y - h) >= vdsq) continue;
				if (x > w && y < 0 && (x - w)*(x - w) +      y * y      >= vdsq) continue;
				if (x > w && y > h && (x - w)*(x - w) + (y - h)*(y - h) >= vdsq) continue;

				//create a new thread to run the lighting process of each enemy
				renderingPool.post(() -> { e.rayTrace(lighting, e.viewDistance, e.color); });
			}
		}

		renderingPool.forceSync();
	}

	public void applyTexture(final int[] pixels) {
		float taskSize = pixels.length/renderingPool.poolSize;
		for (int i = 0; i < renderingPool.poolSize; ++i) {
			final int j = i;
			renderingPool.post(() -> { applyTextureImpl(pixels, PApplet.round(j*taskSize), PApplet.round((j+1)*taskSize)); });
		}

		renderingPool.forceSync();
	}

	private void applyTextureImpl(final int[] pixels, int iBegin, int iEnd) {
		for (int i = iBegin; i < iEnd; ++i) {
			switch (pixels[i]) {
				case Level.FLOOR_NONE:    pixels[i] = textureBlack.pixels[i];   break;
				case Level.FLOOR_RED:     pixels[i] = textureRed.pixels[i];     break;
				case Level.FLOOR_GREEN:   pixels[i] = textureGreen.pixels[i];   break;
{'fraction_non_alphanumeric': 0.13220576461168934, 'fraction_numerical': 0.009607686148919135, 'mean_word_length': 3.78133971291866, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 19, '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': '6708653', 'n_tokens_mistral': 3641, 'n_tokens_neox': 3383, 'n_words': 785}