Below is my raymarcher, mostly adapted from https://www.shadertoy.com/view/llt3R4#
The expected behavior is a projection of a sphere on the screen, but instead it projects
empty space (signified by a yellow pixel, instead of a purple one).
This is my first program written using the x87 coprocessor, so I am unsure if there is anything I'm missing.
A few programmers I know have looked it over without finding a problem, so I am unsure of what to do.
macro bareintro {
org 0x7c00
jmp 0x0:boot
boot:
xor ax, ax
mov ds, ax
mov ss, ax
}
macro zerob b {
times b db 0
}
bareintro
mov ax, 0x13
int 0x10
mov ax, 0xA000
mov es, ax
xor di, di
y_loop:
mov word [ix], 0
mov bx, [iy]
inc bx
cmp bx, 200
je y_end
mov [iy], bx
x_loop:
; first calculate the raymarching direction:
; vec3 rayDirection(float fieldOfView, vec2 size, vec2 fragCoord) {
; vec2 xy = fragCoord - size / 2.0;
; float z = size.y / tan(radians(fieldOfView) / 2.0);
; return normalize(vec3(xy, -z));
; }
fld qword [h]
fld qword [z_diri]
fmulp
fld qword [y_diri]
fild word [iy]
faddp
fld qword [x_diri]
fild word [ix]
faddp
; finally, normalize vector
; do a couple dupes
fld st2
fld st2
fld st2
call len
fdiv st1, st0
fdiv st2, st0
fdivp st3, st0
; store in respective memory locations
fstp qword [dir_x]
fstp qword [dir_y]
fstp qword [dir_z]
call sdts
setc al
add al, 13
stosb
inc word [ix]
cmp word [ix], 319
ja y_loop
jmp x_loop
y_end:
cli
hlt
; int intersect(float rayx, float rayy, float rayz) {
; float depth = MIN_DIST;
; for(int i = 0; i < STEPS; i++) {
; float vx = rayx * depth;
; float vy = rayy * depth;
; float vz = rayz * depth + 5.0;
; float dist = sdf(vx, vy, vz);
; if(dist < EPS) {
; return 1;
; }
; depth += dist;
; if(depth >= MAX_DIST) {
; return 0;
; }
; }
; return 0;
;}
sdts:
fldz
fstp qword [.depth]
mov cx, 255
.loop:
; float dist = sceneSDF(eye + depth * marchingDirection);
; load in direction vector
fld qword [dir_z]
fld qword [dir_y]
fld qword [dir_x]
fld qword [.depth]
fmul st1, st0
fmul st2, st0
fmulp st3, st0
fld qword [eyez]
fld qword [eyey]
fld qword [eyex]
faddp st3, st0
faddp st3, st0
faddp st3, st0
.sdf:
; single sphere for now (len(p) - radius)
call len
; - radius
fld qword [radius]
fsubp st1, st0
.endsdf:
; EPS <=> Dist
fld qword [eps]
fcomip st0, st1
jb .nohit
; cowabunga!
clc
ret
.nohit:
fld qword [.depth]
; MAX <=> Total
faddp
fld qword [.max_depth]
fcomip st0, st1
fstp qword[.depth]
ja .alsofine
stc
ret
.alsofine:
loop .loop
.endloop:
stc
ret
.max_depth:
dq 100.0
.depth:
dq 0.0
; st0 <- sqrt(s0^2 + s1^2 + s2^2)
len:
fmul st0, st0
fxch st1
fmul st0, st0
faddp st1, st0
fxch st1
fmul st0, st0
faddp st1, st0
fsqrt
ret
x_diri:
dq -160.000
y_diri:
dq -100.000
z_diri:
dq 482.8427
dir_x:
dq -160.000
dir_y:
dq -100.000
dir_z:
dq -2.4142135623911343
w:
dq 320.0
h:
dq 200.0
radius:
dq 1.0
eps:
dq 0.001
eyex:
dq 0.0
eyey:
dq 0.0
eyez:
dq 5.0
ix:
dw 0
iy:
dw 0
zerob 510 - ($-$$)
dw 0xaa55
(Here is a working version of the code in C)
#include <math.h>
#include <SDL2/SDL.h>
const int STEPS = 256;
const float MIN_DIST = 0.0;
const float MAX_DIST = 100.0;
const float EPS = 0.001;
float sdf(float x, float y, float z) {
float len = sqrt(x*x + y*y + z*z);
return len - 1.0;
}
int intersect(float rayx, float rayy, float rayz) {
float depth = MIN_DIST;
for(int i = 0; i < STEPS; i++) {
float vx = rayx * depth;
float vy = rayy * depth;
float vz = rayz * depth + 5.0;
float dist = sdf(vx, vy, vz);
if(dist < EPS) {
return 1;
}
depth += dist;
if(depth >= MAX_DIST) {
return 0;
}
}
return 0;
}
int main() {
SDL_Init(SDL_INIT_EVERYTHING);
uint32_t buff[320*200];
SDL_Window* win;
SDL_Renderer* ren;
SDL_CreateWindowAndRenderer(320, 200, 0, &win, &ren);
SDL_Texture* tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC,
320, 200);
for(size_t y = 0; y < 200; y++) {
for(size_t x = 0; x < 320; x++) {
float r_z = -200.0 / 0.41421356237;
float r_y = ((float)y) - 100.0;
float r_x = ((float)x) - 160.0;
float len = sqrt(r_x*r_x + r_y*r_y + r_z*r_z);
r_x /= len;
r_y /= len;
r_z /= len;
buff[y*320 + x] = (-1) * intersect(r_x, r_y, r_z);
}
}
SDL_UpdateTexture(tex, NULL, buff, 320*4);
SDL_RenderCopy(ren, tex, NULL, NULL);
SDL_RenderPresent(ren);
SDL_Event e;
while(1) {
SDL_WaitEvent(&e);
if(e.type == SDL_QUIT) {
SDL_Quit();
return 0;
}
}
return 0;
}
Related
`I am writing a RISC-V code that will reverse the input number. The input number is 10, the output will be the reverse of this 10 numbers.
like this
A_OUT[9] = A_IN[0]
A_OUT[8] = A_IN[1]
A_OUT[7] = A_IN[2]
but my code's reslut is like this
A_OUT[9] =
A_OUT[8] = A_IN[0]
A_OUT[7] = A_IN[1]
how can I fix my code`
.data
A_IN: .space 40
A_OUT: .space 40
PROMPT: .asciz "Enter an integer: "
.text
main:
\# Load addresses of A_IN and A_OUT into two general purpose registers
la x1, A_IN
la x2, A_OUT
\# Initialize loop index and loop bound for reading 10 integers from console
li x3, 0 # loop index
li x4, 10 # loop bound
LOOP1:
\# Print the prompt
la a0, PROMPT
li a7, 4 # ecall number for printing string
ecall
li a7, 5 # ecall number for reading integer
ecall
addi x3, x3, 1 # increment loop index
sw a0, 0(x1)
addi x1, x1, 4 # move to next location in A_IN
bne x3, x4, LOOP1
li x3, 9 # loop index
li x4, 0 # loop bound
LOOP2:
lw a0, 0(x1)
addi x1, x1, -4 # move to previous location in A_IN
sw a0, 0(x2)
addi x2, x2, 4 # move to next location in A_OUT
addi x3, x3, -1
bge x3, x4, LOOP2
li a7, 10 # ecall number for exit
ecall
As you can see, there is a 0 between two 10, how can I fix it
there is something wrong in the second loop, but i couldn't fix it please keep my style coding, just find the problems
I'm trying to write a multithreaded Nagel–Schreckenberg model simulation in c language and have some problems when a thread accesses the data which wasn't calculated yet.
Here is a working code which only parallelizes velocity calculation per line:
#define L 3000 // number of cells in row
#define num_iters 3000 // number of iterations
#define density 0.48 // how many positives
#define vmax 2
#define p 0.2
for (int i = 0; i < num_iters - 1; i++)
{
int temp[L] = {0};
#pragma omp parallel for
for (int x = 0; x < L; x++)
{
if (iterations[i][x] > -1)
{
int vi = iterations[i][x]; // velocity of previews iteration
int d = 1; // index of the next vehicle
while (iterations[i][(x + d) % L] < 0)
d++;
int vtemp = min(min(vi + 1, d - 1), vmax); // increase speed, but avoid hitting the next car
int v = r2() < p ? max(vtemp - 1, 0) : vtemp; // stop the vehicle with probability p
temp[x] = v;
}
}
for (int x = 0; x < L; x++) // write the velocities to the next line
{
if (iterations[i][x] > -1)
{
int v = temp[x];
iterations[i + 1][(x + v) % L] = v;
}
}
}
This works fine, but it's not fast enough. I'm trying to use convolution to increase the performance, but it can't read neighbor thread's data half of the time because it wasn't calculated yet. Here is the code I used:
#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <string.h>
#include <sys/time.h>
#define L 4000 // number of cells in row
#define num_iters 4000 // number of iterations
#define density 0.48 // how many positives
#define vmax 2
#define p 0.2
#define BLOCKS_Y 4
#define BLOCKS_X 4
#define BLOCKSIZEY (L / BLOCKS_Y)
#define BLOCKSIZEX (L / BLOCKS_X)
time_t t;
#ifndef min
#define min(a, b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef max
#define max(a, b) (((a) > (b)) ? (a) : (b))
#endif
void shuffle(int *array, size_t n)
{
if (n > 1)
{
size_t i;
for (i = 0; i < n - 1; i++)
{
size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
int t = array[j];
array[j] = array[i];
array[i] = t;
}
}
}
double r2()
{
return (double)rand() / (double)RAND_MAX;
}
void writeImage(int *iterations[], char filename[])
{
int h = L;
int w = num_iters;
FILE *f;
unsigned char *img = NULL;
int filesize = 54 + 3 * w * h;
img = (unsigned char *)malloc(3 * w * h);
memset(img, 0, 3 * w * h);
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
int x = i;
int y = (h - 1) - j;
int color = iterations[i][j] == 0 ? 0 : 255;
img[(x + y * w) * 3 + 2] = (unsigned char)(color);
img[(x + y * w) * 3 + 1] = (unsigned char)(color);
img[(x + y * w) * 3 + 0] = (unsigned char)(color);
}
}
unsigned char bmpfileheader[14] = {'B', 'M', 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0};
unsigned char bmpinfoheader[40] = {40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 24, 0};
unsigned char bmppad[3] = {0, 0, 0};
bmpfileheader[2] = (unsigned char)(filesize);
bmpfileheader[3] = (unsigned char)(filesize >> 8);
bmpfileheader[4] = (unsigned char)(filesize >> 16);
bmpfileheader[5] = (unsigned char)(filesize >> 24);
bmpinfoheader[4] = (unsigned char)(w);
bmpinfoheader[5] = (unsigned char)(w >> 8);
bmpinfoheader[6] = (unsigned char)(w >> 16);
bmpinfoheader[7] = (unsigned char)(w >> 24);
bmpinfoheader[8] = (unsigned char)(h);
bmpinfoheader[9] = (unsigned char)(h >> 8);
bmpinfoheader[10] = (unsigned char)(h >> 16);
bmpinfoheader[11] = (unsigned char)(h >> 24);
f = fopen(filename, "wb");
fwrite(bmpfileheader, 1, 14, f);
fwrite(bmpinfoheader, 1, 40, f);
for (int i = 0; i < h; i++)
{
fwrite(img + (w * (h - i - 1) * 3), 3, w, f);
fwrite(bmppad, 1, (4 - (w * 3) % 4) % 4, f);
}
free(img);
fclose(f);
}
void simulation()
{
printf("L=%d, num_iters=%d\n", L, num_iters);
int z = 0;
z++;
int current_index = 0;
int success_moves = 0;
const int cars_num = (int)(density * L);
int **iterations = (int **)malloc(num_iters * sizeof(int *));
for (int i = 0; i < num_iters; i++)
iterations[i] = (int *)malloc(L * sizeof(int));
for (int i = 0; i < L; i++)
{
iterations[0][i] = i <= cars_num ? 0 : -1;
}
shuffle(iterations[0], L);
for (int i = 0; i < num_iters - 1; i++)
for (int x = 0; x < L; x++)
iterations[i + 1][x] = -1;
double *randoms = (double *)malloc(L * num_iters * sizeof(double));
for (int i = 0; i < L * num_iters; i++) {
randoms[i] = r2();
}
#pragma omp parallel for collapse(2)
for (int blocky = 0; blocky < BLOCKS_Y; blocky++)
{
for (int blockx = 0; blockx < BLOCKS_X; blockx++)
{
int ystart = blocky * BLOCKSIZEY;
int yend = ystart + BLOCKSIZEY;
int xstart = blockx * BLOCKSIZEX;
int xend = xstart + BLOCKSIZEX;
for (int y = ystart; y < yend; y++)
{
for (int x = xstart; x < xend; x++)
{
if (iterations[y][x] > -1)
{
int vi = iterations[y][x];
int d = 1;
int start = (x + d) % L;
int i;
for (i = start; i < L && iterations[y][i] < 0; ++i);
d += i - start;
if (i == L)
{
for (i = 0; i < start && iterations[y][i] < 0; ++i);
d += i;
}
int vtemp = min(min(vi + 1, d - 1), vmax);
int v = randoms[x * y] < p ? max(vtemp - 1, 0) : vtemp;
iterations[y + 1][(x + v) % L] = v;
}
}
}
}
}
if (L <= 4000)
writeImage(iterations, "img.bmp");
free(iterations);
}
void main() {
srand((unsigned)time(&t));
simulation();
}
As you can see, as the second block gets calculated the first one didn't probably calculate yet which produces that empty space.
I think it's possible to solve this with the convolution, but I'm just doing something wrong and I'm not sure what. If you could give any advice on how to fix this problem, I would really appreciate it.
There is a race condition in the second code because iterations can be read by a thread and written by another. More specifically, iterations[y + 1][(x + v) % L] = v set a value that another thread should read when checking iterations[y][x] or iterations[y][(x + d) % L] when two threads are working on consecutive y values (of two consecutive blocky values).
Moreover, the r2 function have to be thread-safe. It appears to be a random number generator (RNG), but such random function is generally implemented using global variables that are often not thread-safe. One simple and efficient solution is to use thread_local variables instead. An alternative solution is to explicitly pass in parameter a mutable state to the random function. The latter is a good practice when you design parallel applications since it makes visible the mutation of an internal state and it provides way to better control the determinism of the RNG.
Besides this, please note that modulus are generally expensive, especially if L is not a compile-time constant. You can remove some of them by pre-computing the remainder before a loop or splitting a loop so to perform checks only near the boundaries. Here is an (untested) example for the while:
int start = (x + d) % L;
int i;
for(i=start ; i < L && iterations[y][i] < 0 ; ++i);
d += i - start;
if(i == L) {
for(i=0 ; i < start && iterations[y][i] < 0 ; ++i);
d += i;
}
Finally, please note that the blocks should be divisible by 4. Otherwise, the current code is not valid (a min/max clamping is likely needed).
I'm creating a rtos system, with system tick (1ms) for real-time and trigger pendsv-which used for switch task. Priority of systick and pendsv same as 0 (group = 16; <SCB.AIRCR.PRIGROUP = 0b011>). PendSv also trigger by <SCB.ICSR.PENDSVSET = 1> when current task is sleep.
Sometime, PendSV and SysTick occurs at the same time, and SysTick is executed first, then PendSv. But after PendSV executed, SysTick occurs again.
I was tried by change priority, and clear <SCB.ICSR.PENDSTCLR = 1> in system tick handle, nothing changed.
Do you know why, and how to fix that?
logic_analyzer
asm code:
//#define USING_PUSH_POP
EXTERN GPIOA_BITBAND
EXTERN RTOS_GetCurrentCPU //CPU_TypeDef* RTOS_GetCurrentCPU();
EXTERN RTOS_GetNextCPU //CPU_TypeDef* RTOS_GetNextCPU();
PUBLIC PendSV_Handler
PUBLIC RTOS_ASM_Begin //void RTOS_ASM_ASM_Begin(CPU_TypeDef* CPU);
SECTION rtos_function:CODE
PendSV_Handler:
//PUSH in SP when interrupt
//Here - R0 - R1 - R2 - R3 - R12 - BACK_LR - BACK_PC - EAPSR
PUSH {LR}
LDR.W R0, =GPIOA_BITBAND //(1)
MOV R1, #1
STR R1, [R0, #0x180]
BL RTOS_GetCurrentCPU
CMP R0, #0
BEQ BREAK //if (NULL): back to current task
BL RTOS_ASM_SaveCurrentCPU
BL RTOS_GetNextCPU
CMP R0, #0
BEQ BREAK //if (NULL): back to current task
B RTOS_ASM_SwitchNextCPU
BREAK:
POP {PC}
//===================================
RTOS_ASM_Begin: //R0: CPU_TypeDef* MainCPU
PUSH {R1, LR}
STR LR , [R0, #28]
STR R1 , [R0, #24]
ADDS R1 , R0, #24
MOV SP , R1
BL RTOS_ASM_SaveCurrentCPU
POP {R1, PC}
//===================================
RTOS_ASM_SaveCurrentCPU: //R0: CPU_TypeDef* CurrentCPU
#ifdef USING_PUSH_POP
MOVS R1 , SP
STR SP , [R0, #(4 * 16)]
ADDS R0 , R0, #(4 * 16)
MOVS SP , R0
PUSH {R4-R11}
MOVS SP , R1
#else
STR R4 , [R0, #(4 * 8 )]
STR R5 , [R0, #(4 * 9 )]
STR R6 , [R0, #(4 * 10)]
STR R7 , [R0, #(4 * 11)]
STR R8 , [R0, #(4 * 12)]
STR R9 , [R0, #(4 * 13)]
STR R10, [R0, #(4 * 14)]
STR R11, [R0, #(4 * 15)]
STR SP , [R0, #(4 * 16)]
#endif
BX LR
//===================================
RTOS_ASM_SwitchNextCPU: //R0: CPU_TypeDef* NextCPU
#ifdef USING_PUSH_POP
ADDS R1 , R0, #(4 * 8)
MOVS SP , R1
POP {R4-R11}
LDR SP , [R0, #(4 * 16)]
#else
LDR R4 , [R0, #(4 * 8 )]
LDR R5 , [R0, #(4 * 9 )]
LDR R6 , [R0, #(4 * 10)]
LDR R7 , [R0, #(4 * 11)]
LDR R8 , [R0, #(4 * 12)]
LDR R9 , [R0, #(4 * 13)]
LDR R10, [R0, #(4 * 14)]
LDR R11, [R0, #(4 * 15)]
LDR SP , [R0, #(4 * 16)]
#endif
LDR.W R0, =GPIOA_BITBAND //(2)
MOV R1, #0
STR R1, [R0, #0x180]
POP {PC}
//===================================
END
system tick handle:
void SysTick_Handler()
{
GPIOB_BITBAND.ODR._10 ^= 1; //(3)
System.MsTick++;
if (TaskManager.Running)
RTOS_SWITCH_TRIGGER();
//SCB.ICSR.REG = BIT25;
SCB.ICSR.BITS.PENDSTCLR = 1;
}
My solution now is using SVC instead of PendSV
Now, I know similar questions have been asked. But none of the answers has helped me to find the result I need.
Following situation:
We have a line with a point-of-origin (PO), given as lx, ly. We also have an angle for the line in that it exits PO, where 0° means horizontally to the right, positive degrees mean clockwise. The angle is in [0;360[. Additionally we have the length of the line, since it is not infinitely long, as len.
There is also a circle with the given center-point (CP), given as cx, cy. The radius is given as cr.
I now need a function that takes these numbers as parameters and returns the distance of the closest intersection between line and circle to the PO, or -1 if no intersection occures.
My current approach is a follows:
float getDistance(float lx, float ly, float angle, float len, float cx, float cy, float cr) {
float nlx = lx - cx;
float nly = ly - cy;
float m = tan(angle);
float b = (-lx) * m;
// a = m^2 + 1
// b = 2 * m * b
// c = b^2 - cr^2
float[] x_12 = quadraticFormula(sq(m) + 1, 2*m*b, sq(b) - sq(cr));
// if no intersections
if (Float.isNaN(x_12[0]) && Float.isNaN(x_12[1]))
return -1;
float distance;
if (Float.isNaN(x_12[0])) {
distance = (x_12[1] - nlx) / cos(angle);
} else {
distance = (x_12[0] - nlx) / cos(angle);
}
if (distance <= len) {
return distance;
}
return -1;
}
// solves for x
float[] quadraticFormula(float a, float b, float c) {
float[] results = new float[2];
results[0] = (-b + sqrt(sq(b) - 4 * a * c)) / (2*a);
results[1] = (-b - sqrt(sq(b) - 4 * a * c)) / (2*a);
return results;
}
But the result is not as wished. Sometimes I do get a distance returned, but that is rarely correct, there often isn't even an intersection occuring. Most of the time no intersection is returned though, although there should be one.
Any help would be much appreciated.
EDIT:
I managed to find the solution thanks to MBo's answer. Here is the content of my finished getDistance(...)-function - maybe somebody can be helped by it:
float nlx = lx - cx;
float nly = ly - cy;
float dx = cos(angle);
float dy = sin(angle);
float[] results = quadraticFormula(1, 2*(nlx*dx + nly*dy), sq(nlx)+sq(nly)-sq(cr));
float dist = -1;
if (results[0] >= 0 && results[0] <= len)
dist = results[0];
if (results[1] >= 0 && results[1] <= len && results[1] < results[0])
dist = results[1];
return dist;
Using your nlx, nly, we can build parametric equation of line segment
dx = Cos(angle)
dy = Sin(Angle)
x = nlx + t * dx
y = nly + t * dy
Condition of intersection with circumference:
(nlx + t * dx)^2 + (nly + t * dy)^2 = cr^2
t^2 * (dx^2 + dy^2) + t * (2*nlx*dx + 2*nly*dy) + nlx^2+nly^2-cr^2 = 0
so we have quadratic equation for unknown parameter t with
a = 1
b = 2*(nlx*dx + nly*dy)
c = nlx^2+nly^2-cr^2
solve quadratic equation, find whether t lies in range 0..len.
// https://openprocessing.org/sketch/8009#
// by https://openprocessing.org/user/54?view=sketches
float circleX = 200;
float circleY = 200;
float circleRadius = 100;
float lineX1 = 350;
float lineY1 = 350;
float lineX2, lineY2;
void setup() {
size(400, 400);
ellipseMode(RADIUS);
smooth();
}
void draw() {
background(204);
lineX2 = mouseX;
lineY2 = mouseY;
if (circleLineIntersect(lineX1, lineY1, lineX2, lineY2, circleX, circleY, circleRadius) == true) {
noFill();
}
else {
fill(255);
}
ellipse(circleX, circleY, circleRadius, circleRadius);
line(lineX1, lineY1, lineX2, lineY2);
}
// Code adapted from Paul Bourke:
// http://local.wasp.uwa.edu.au/~pbourke/geometry/sphereline/raysphere.c
boolean circleLineIntersect(float x1, float y1, float x2, float y2, float cx, float cy, float cr ) {
float dx = x2 - x1;
float dy = y2 - y1;
float a = dx * dx + dy * dy;
float b = 2 * (dx * (x1 - cx) + dy * (y1 - cy));
float c = cx * cx + cy * cy;
c += x1 * x1 + y1 * y1;
c -= 2 * (cx * x1 + cy * y1);
c -= cr * cr;
float bb4ac = b * b - 4 * a * c;
//println(bb4ac);
if (bb4ac < 0) { // Not intersecting
return false;
}
else {
float mu = (-b + sqrt( b*b - 4*a*c )) / (2*a);
float ix1 = x1 + mu*(dx);
float iy1 = y1 + mu*(dy);
mu = (-b - sqrt(b*b - 4*a*c )) / (2*a);
float ix2 = x1 + mu*(dx);
float iy2 = y1 + mu*(dy);
// The intersection points
ellipse(ix1, iy1, 10, 10);
ellipse(ix2, iy2, 10, 10);
float testX;
float testY;
// Figure out which point is closer to the circle
if (dist(x1, y1, cx, cy) < dist(x2, y2, cx, cy)) {
testX = x2;
testY = y2;
} else {
testX = x1;
testY = y1;
}
if (dist(testX, testY, ix1, iy1) < dist(x1, y1, x2, y2) || dist(testX, testY, ix2, iy2) < dist(x1, y1, x2, y2)) {
return true;
} else {
return false;
}
}
}
I need to create an effect, that radially distorts a bitmap, by stretching or shrinking its "layers of pixels" radially (as shown on the image):
http://i.stack.imgur.com/V6Voo.png
by colored circles (their thickness) is shown the transform, that is applied to the image
What approach should I take? I have a bitmap (array of pixels) and an another bitmap, that should be the result of such a filter applied (as a result, there should be some kind of a round water ripple on the bitmap).
Where could I read about creating such effects?
Thank you.
Try to look here
http://www.jhlabs.com/ip/blurring.html
Zoom and Spin Blur
it is Java but nevertheless it could be fit to your request.
Well, the most accurate results would come from mapping the euclidean coordinates to a polar matrix. Then you would very easily be able to stretch them out. Then just translate them back to a euclidean representation and save. I'll write and edit with some code in a second.
Alright I got a bit carried away but here's my code. It will take a bitmap, convert it to and from polar coordinates and save it. now, radial based distortion should be a breeze.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#define PI 3.141592654
#define C_R 1000
#define C_S 1000
#define C_M 2000
typedef struct{ int r,g,b; } color;
typedef struct{ int t; color* data; int w, h; } bitmap;
typedef struct{ int t; color* data; int r, s, w, h; } r_bitmap;
bitmap* bmp_load_from_file( const char* fname ){
FILE* b = fopen( fname, "rb" );
if( b <= 0 ) return 0;
int num;
fscanf( b, "BM%n", &num );
if( num < 2 ) return 0;
struct{ int size, reserved, offset;
int hsize, wid, hig, planes:16, bpp:16, comp, bmpsize, hres, vres, colors, important; } head;
fread( &head, 13, 4, b );
bitmap* bmp = malloc( sizeof( bitmap ) );
bmp->data = malloc( head.wid * head.hig * sizeof( color ) );
bmp->w = head.wid;
bmp->h = head.hig;
for( int y = head.hig - 1; y >= 0; --y ){
int x;
for( x = 0; x < head.wid; ++x ){
color t;
t.r = fgetc( b );
t.g = fgetc( b );
t.b = fgetc( b );
bmp->data[x+y*bmp->w] = t;
}
x*=3;
while( x%4 != 0 ){
++x;
fgetc( b );
}
}
bmp->t = 0;
fclose( b );
return bmp;
}
void bmp_save( const char* fname, bitmap* bmp ){
FILE* b = fopen( fname, "wb" );
if( b <= 0 ) return 0;
struct{ int size, reserved, offset;
int hsize, wid, hig, planes:16, bpp:16, comp, bmpsize, hres, vres, colors, important; } head;
fprintf( b, "BM" );
head.size = 3 * (bmp->w+4)/4*4 * bmp->h + 54;
head.offset = 54;
head.hsize = 40;
head.wid = bmp->w;
head.hig = bmp->h;
head.planes = 1;
head.bpp = 24;
head.comp = 0;
head.bmpsize = 3 * (bmp->w+4)/4*4 * bmp->h;
head.hres = 72;
head.vres = 72;
head.colors = 0;
head.important = 0;
fwrite( &head, 13, 4, b );
for( int y = bmp->h - 1; y >= 0; --y ){
int x;
for( x = 0; x < bmp->w; ++x ){
fputc( bmp->data[x + y * bmp->w].r, b );
fputc( bmp->data[x + y * bmp->w].g, b );
fputc( bmp->data[x + y * bmp->w].b, b );
}
x*=3;
while( x % 4 != 0 ){
++x;
fputc(0, b);
}
}
fclose( b );
}
color color_mix( color a, color b, int offset ){ /*offset is a value between 0 and 255 to determine the weight. the lower it is the more color a gets*/
//if( offset > 255 || offset < 0)
//printf("%i\t", offset);
a.r += ( b.r - a.r ) * offset / 255;
a.g += ( b.g - a.g ) * offset / 255;
a.b += ( b.b - a.b ) * offset / 255;
return a;
}
r_bitmap* bmp_to_r( bitmap* b ){
r_bitmap* r = malloc( sizeof( r_bitmap ) );
r->t = 1;
int radius = sqrt( b->w * b->w + b->h * b->h ) / 2 * C_R / C_M + 2;
int step = C_S * ( b->w + b->h ) / C_M;
r->data = malloc( radius * step * sizeof( color ) );
r->r = radius;
r->s = step;
r->w = b->w;
r->h = b->h;
color black = {0, 0, 0};
for( double i = 0; i < radius; ++ i ){
for( double j = 0; j < step; ++j ){
double x = i * C_M * cos( 2 * PI * j / step ) / C_R + b->w / 2;
double y = i * C_M * sin( 2 * PI * j / step ) / C_R + b->h / 2;
int ix = x;
int iy = y;
if( x < 0 || x >= b->w || y < 0 || y >= b->h )
r->data[(int)(j + i * step)] = black;
else{
color tmp = b->data[ix + iy * b->w];
if( iy < b->h - 1 ){
int off = 255 * (y - iy);
tmp = color_mix( tmp, b->data[ix + (iy+1) * b->w], off );
}
if( ix < b->w - 1 ){
int off = 255 * ( x - ix );
tmp = color_mix( tmp, b->data[ix +1 + iy * b->w], off );
}
r->data[(int)(j + i * step)] = tmp;
}
}
}
return r;
}
bitmap* bmp_from_r( r_bitmap* r ){
bitmap* b = malloc( sizeof( bitmap ) );
b->t = 0;
b->data = malloc( r->w * r->h * sizeof( color ) );
b->w = r->w;
b->h = r->h;
for( int y = 0; y < b->h; ++y ){
for( int x = 0; x < b->w; ++x ){
int tx = x - b->w/2;
int ty = y - b->h/2;
double rad = sqrt( tx*tx+ty*ty ) * C_R / C_M;
double s = atan2( ty, tx );
if( s < 0 ) s += 2 * PI;
s *= r->s / ( 2 * PI );
int is = s;
int irad = rad;
color tmp = r->data[(int)(is + irad * r->s)];
/*if( x > 0 && x < r->w - 1 && y > 0 && y < r->h - 1 ){
tmp = color_mix(tmp, r->data[((int)(is+1)%r->s + irad * r->s)], abs(255* rad - floor(rad)));
tmp = color_mix(tmp, r->data[(is + (irad + 1) * r->s)], abs(255* s - floor(s)));
}*/
b->data[x+y*b->w] = tmp;
}
}
return b;
}
int main( ) {
bitmap* b = bmp_load_from_file( "foo.bmp" );
r_bitmap* r = bmp_to_r( b );
bitmap* c = bmp_from_r( r );
bmp_save( "lol.bmp", c );
}