I'm trying to create a smooth "wave" when the mouse moves over isometric logo shape.
I've created in in processing now I'm trying to recreate it in THREE.js
The shape acts strangely - the shape doesn't look as smooth when elevated compared to the processing sketch. If you look at the edges you can see segments that are not supposed to be there. I'm not sure what causes this.
Basically the shape is created through a loops that goes over 2 arrays:
for (var i = 0; i < xpos0.length; i++) {
shape.lineTo(xpos0[i], ypos0[i]);
}
Then it animates through another for loop that checks the distance between verteces[i].x and mouse location intersection with the ground
for (let p = 0; p < mesh.geometry.vertices.length; p=p+1) {
let delta = Math.abs(mesh.geometry.vertices[p].x - intersects[0].point.x);
mesh.geometry.vertices[p].z = bump2(-2, 2000, -1, 2, delta);
}
z value is calculated through this function:
function bump2(a,b,c,d,xval) {
xval = parseFloat(xval);
// console.log(typeof xval);
return Math.exp(a / (-xval * xval / b + c) + d) * -1;
}
https://codepen.io/NotYetDesignLab/pen/JjYaqRJ
How it looks on THREE.JS
notice how some segments appear "broken", like it's made of stiff parts instead of the many points that make up the segment in the array and give the illusion of "paper".
THIS IS HOW IT'S SUPPOSED TO LOOK: (Processing/java)
This has been done using Processing. Notice how the elevation of the edges is smooth and not broken.
Related
Alright I think I've mostly figured out how the MagicTile works, the source code at least (not really the Math as much yet). It all begins with the build and render calls in the MainForm.cs. It generates a tessellation like this:
First, it "generates" the tessellation. Since MagicTile is a Rubic's cube-like game, I guess it just statically computes all of the tiles up front. It does this by starting with a central tile, and reflecting its polygon (and the polygon's segments and points) using some sort of math which I've read about several times but I couldn't explain. Then it appears they allow rotations of the tessellation, where they call code like this in the "renderer":
Polygon p = sticker.Poly.Clone();
p.Transform( m_mouseMotion.Isometry );
Color color = GetStickerColor( sticker );
GLUtils.DrawConcavePolygon( p, color, GrabModelTransform() );
They track the mouse position, like if you are dragging, and somehow that is used to create an "isometry" to augment / transform the overall tessellation. So then we transform the polygon using that isometry. _It appears they only do the central tile and 1 or 2 levels after that, but I can't quite tell, I haven't gotten the app to run and debug yet (it's also in C# and that's a new language for me, coming from TypeScript). The Transform function digs down like this (here it is in TypeScript, as I've been converting it):
TransformIsometry(isometry: Isometry) {
for (let s of this.Segments) {
s.TransformIsometry(isometry)
}
this.Center = isometry.Apply(this.Center)
}
That goes into the transform for the segments here:
/// <summary>
/// Apply a transform to us.
/// </summary>
TransformInternal<T extends ITransform>(transform: T) {
// NOTES:
// Arcs can go to lines, and lines to arcs.
// Rotations may reverse arc directions as well.
// Arc centers can't be transformed directly.
// NOTE: We must calc this before altering the endpoints.
let mid: Vector3D = this.Midpoint
if (UtilsInfinity.IsInfiniteVector3D(mid)) {
mid = this.P2.MultiplyWithNumber(UtilsInfinity.FiniteScale)
}
mid = UtilsInfinity.IsInfiniteVector3D(this.P1)
? this.P2.MultiplyWithNumber(UtilsInfinity.FiniteScale)
: this.P1.MultiplyWithNumber(UtilsInfinity.FiniteScale)
this.P1 = transform.ApplyVector3D(this.P1)
this.P2 = transform.ApplyVector3D(this.P2)
mid = transform.ApplyVector3D(mid)
// Can we make a circle out of the transformed points?
let temp: Circle = new Circle()
if (
!UtilsInfinity.IsInfiniteVector3D(this.P1) &&
!UtilsInfinity.IsInfiniteVector3D(this.P2) &&
!UtilsInfinity.IsInfiniteVector3D(mid) &&
temp.From3Points(this.P1, mid, this.P2)
) {
this.Type = SegmentType.Arc
this.Center = temp.Center
// Work out the orientation of the arc.
let t1: Vector3D = this.P1.Subtract(this.Center)
let t2: Vector3D = mid.Subtract(this.Center)
let t3: Vector3D = this.P2.Subtract(this.Center)
let a1: number = Euclidean2D.AngleToCounterClock(t2, t1)
let a2: number = Euclidean2D.AngleToCounterClock(t3, t1)
this.Clockwise = a2 > a1
} else {
// The circle construction fails if the points
// are colinear (if the arc has been transformed into a line).
this.Type = SegmentType.Line
// XXX - need to do something about this.
// Turn into 2 segments?
// if( UtilsInfinity.IsInfiniteVector3D( mid ) )
// Actually the check should just be whether mid is between p1 and p2.
}
}
So as far as I can tell, this will adjust the segments based on the mouse position, somehow. Mouse position isometry updating code is here.
So it appears they don't have the functionality to "move" the tiling, like if you were walking on it, like in HyperRogue.
So after having studied this code for a few days, I am not sure how to move or walk along the tiles, moving the outer tiles toward the center, like you're a giant walking on Earth.
First small question, can you do this with MagicTile? Can you somehow update the tessellation to move a different tile to the center? (And have a function which I could plug a tween/animation into so it animates there). Or do I need to write some custom new code? If so, what do I need to do roughly speaking, maybe some pseudocode?
What I imagine is, user clicks on the outer part of the tessellation. We convert that click data to the tile index in the tessellation, then basically want to do tiling.moveToCenter(tile), but frame-by-frame-animation, so not quite sure how that would work. But that moveToCenter, what would that do in terms of the MagicTile rendering/tile-generating code?
As I described in the beginning, it first generates the full tessellation, then only updates 1-3 layers of the tiles for their puzzles. So it's like I need to first shift the frame of reference, and recompute all the potential visible tiles, somehow not recreating the ones that were already created. I don't quite see how that would work, do you? Once the tiles are recomputed, then I just re-render and it should show the updated center.
Is it a simple matter of calling some code like this again, for each tile, where the isometry is somehow updated with a border-ish position on the tessellation?
Polygon p = sticker.Poly.Clone();
p.Transform( m_mouseMotion.Isometry );
Or must I do something else? I can't quite see the full picture yet.
Or is that what these 3 functions are doing! TypeScript port of the C# MagicTile:
// Move from a point p1 -> p2 along a geodesic.
// Also somewhat from Don.
Geodesic(g: Geometry, p1: Complex, p2: Complex) {
let t: Mobius = Mobius.construct()
t.Isometry(g, 0, p1.Negate())
let p2t: Complex = t.ApplyComplex(p2)
let m2: Mobius = Mobius.construct()
let m1: Mobius = Mobius.construct()
m1.Isometry(g, 0, p1.Negate())
m2.Isometry(g, 0, p2t)
let m3: Mobius = m1.Inverse()
this.Merge(m3.Multiply(m2.Multiply(m1)))
}
Hyperbolic(g: Geometry, fixedPlus: Complex, scale: number) {
// To the origin.
let m1: Mobius = Mobius.construct()
m1.Isometry(g, 0, fixedPlus.Negate())
// Scale.
let m2: Mobius = Mobius.construct()
m2.A = new Complex(scale, 0)
m2.C = new Complex(0, 0)
m2.B = new Complex(0, 0)
m2.D = new Complex(1, 0)
// Back.
// Mobius m3 = m1.Inverse(); // Doesn't work well if fixedPlus is on disk boundary.
let m3: Mobius = Mobius.construct()
m3.Isometry(g, 0, fixedPlus)
// Compose them (multiply in reverse order).
this.Merge(m3.Multiply(m2.Multiply(m1)))
}
// Allow a hyperbolic transformation using an absolute offset.
// offset is specified in the respective geometry.
Hyperbolic2(
g: Geometry,
fixedPlus: Complex,
point: Complex,
offset: number,
) {
// To the origin.
let m: Mobius = Mobius.construct()
m.Isometry(g, 0, fixedPlus.Negate())
let eRadius: number = m.ApplyComplex(point).Magnitude
let scale: number = 1
switch (g) {
case Geometry.Spherical:
let sRadius: number = Spherical2D.e2sNorm(eRadius)
sRadius = sRadius + offset
scale = Spherical2D.s2eNorm(sRadius) / eRadius
break
case Geometry.Euclidean:
scale = (eRadius + offset) / eRadius
break
case Geometry.Hyperbolic:
let hRadius: number = DonHatch.e2hNorm(eRadius)
hRadius = hRadius + offset
scale = DonHatch.h2eNorm(hRadius) / eRadius
break
default:
break
}
this.Hyperbolic(g, fixedPlus, scale)
}
Matrix operations performed on the GPU can be pretty hard to debug because GPU operations don't really allow for console logs.
I've written one designed for a real time 2D rendering engine based on a very simple form of I guess what could be called ray casting and am having trouble figuring out what's wrong with it (it's outputting [0,0,0,255,0,0,0,255,...] instead of populating colors).
this.thread.x is the index of the current unit (color channel) in the matrix being operated on.
scene is a buffer made up of 6-unit clumps, each value containing, in order:
The type of entity, always 1 for "sprite" in this case.
The sprite ID, corresponding the the index in this.constants.textures containing the buffer for the entity's sprite.
X offset, the left edge of the sprite
Y offset, the top edge of the sprite
width of the sprite
height of the sprite
bufferWidth is the width of the render area multiplied by 4 channels.
this.constants.textures is an array containing buffers of each sprite which the sprite IDs from the scene refer to.
Note: For those curious, this is being done with GPU.js, a JavaScript lib that converts a JS func into GLSL code to be run via WebGL.
function(scene, sceneLength, bufferWidth) {
var channel = this.thread.x % 4;
if (channel === 3) {
return 255;
}
var x = this.thread.x % bufferWidth;
var y = Math.floor(this.thread.x / bufferWidth);
for (let i1 = 0; i1 < sceneLength; i1 += 6) {
var id = scene[i1 + 1];
var x1 = scene[i1 + 2];
var y1 = scene[i1 + 3];
var w1 = scene[i1 + 4];
var h1 = scene[i1 + 5];
var r1 = scene[i1 + 6];
var offsetX1 = x1 - x;
if (offsetX1 > 0 && offsetX1 < w1) {
var offsetY1 = y1 - y;
if (offsetY1 > 0 && offsetY1 < h1) {
var c1 = offsetY1 * w1 * 4 + offsetX1 * 4;
var c1R = c1 - (c1 % 4);
var c1A = c1R + 3;
if (this.constants.textures[id][c1A] != 0) {
return this.constants.textures[id][c1];
}
}
}
}
return 0;
}
Explanation for the concept I'm trying to implement:
With a matrix operation, when you want to draw a sprite if you were to perform a pass on the entire render area, you'd be doing far more work than necessary. If you break the rendering area down into chunks and only update the sections involved in the sprite being drawn, that would be a fairly decent way to do it. It would certainly be good enough for real time game rendering. This would be a multi-pass approach, where sprites are rendered one at a time.
Alternatively, for what seems to me to be the most optimal approach possible, instead of that, we can use a single-pass approach that performs a single matrix operation for the entire rendering area, evaluating for each color channel what should be there based on doing a very basic form of collision detection with each sprite in the scene and the relevant pixel in that sprite.
You're calculating your sprite offsets backwards, the calculations should be:
var offsetX1 = x - x1;
and
var offsetY1 = y - y1;
The offsets should increase as x and y increase (assuming the sprite co-ordinates have the same co-ordinate system as the screen co-ordinates), so you shouldn't be subtracting x and y.
I am trying to store the motion detected from optical flow for frames in a video sequence and then use these stored motion vectors in order to predict the already known frames using just the first frame as a reference. I am currently using two processing sketches - the first sketch draws a motion vector for every pixel grid (each of width and height 10 pixels). This is done for every frame in the video sequence. The vector is only drawn in a grid if there is sufficient motion detected. The second sketch aims to reconstruct the video frames crudely from just the initial frame of the video sequence combined with information about the motion vectors got from the first sketch.
My approach so far is as follows: I am able to determine the size, position and direction of each motion vector drawn in the first sketch from four variables. By creating four arrays (two for the motion vector's x and y coordinate and another two for its length in the x and y direction), every time a motion vector is drawn I can append each of the four variables to the arrays mentioned above. This is done for each pixel grid throughout an entire frame where the vector is drawn and for each frame in the sequence - via for loops. Once the arrays are full, I can then save them to a text file as a list of strings. I then load these strings from the text file into the second sketch, along with the first frame of the video sequence. I load the strings into variables within a while loop in the draw function and convert them back into floats. I increment a variable by one each time the draw function is called - this moves on to the next frame (I used a specific number as a separator in my text-files which appears at the end of every frame - the loop searches for this number and then increments the variable by one, thus breaking the while loop and the draw function is called again for the subsequent frame). For each frame, I can draw 10 by 10 pixel boxes and move then by the parameters got from the text files in the first sketch. My problem is simply this: How do I draw the motion of a particular frame without letting what I've have blitted to the screen in the previous frame affect what will be drawn for the next frame. My only way of getting my 10 by 10 pixel box is by using the get() function which gets pixels that are already drawn to the screen.
Apologies for the length and complexity of my question. Any tips would be very much appreciated! I will add the code for the second sketch. I can also add the first sketch if required, but it's rather long and a lot of it is not my own. Here is the second sketch:
import processing.video.*;
Movie video;
PImage [] naturalMovie = new PImage [0];
String xlengths [];
String ylengths [];
String xpositions [];
String ypositions [];
int a = 0;
int c = 0;
int d = 0;
int p;
int gs = 10;
void setup(){
size(640, 480, JAVA2D);
xlengths = loadStrings("xlengths.txt");
ylengths = loadStrings("ylengths.txt");
xpositions = loadStrings("xpositions.txt");
ypositions = loadStrings("ypositions.txt");
video = new Movie(this, "sample1.mov");
video.play();
rectMode(CENTER);
}
void movieEvent(Movie m) {
m.read();
PImage f = createImage(m.width, m.height, ARGB);
f.set(0, 0, m);
f.resize(width, height);
naturalMovie = (PImage []) append(naturalMovie, f);
println("naturalMovie length: " + naturalMovie.length);
p = naturalMovie.length - 1;
}
void draw() {
if(naturalMovie.length >= p && p > 0){
if (c == 0){
image(naturalMovie[0], 0, 0);
}
d = c;
while (c == d && c < xlengths.length){
float u, v, x0, y0;
u = float(xlengths[a]);
v = float(ylengths[a]);
x0 = float(xpositions[a]);
y0 = float(ypositions[a]);
if (u != 1.0E-19){
//stroke(255,255,255);
//line(x0,y0,x0+u,y0+v);
PImage box;
box = get(int(x0-gs/2), int(y0 - gs/2), gs, gs);
image(box, x0-gs/2 +u, y0 - gs/2 +v, gs, gs);
if (a < xlengths.length - 1){
a += 1;
}
}
else if (u == 1.0E-19){
if (a < xlengths.length - 1){
c += 1;
a += 1;
}
}
}
}
}
Word to the wise: most people aren't going to read that wall of text. Try to "dumb down" your posts so they get to the details right away, without any extra information. You'll also be better off if you post an MCVE instead of only giving us half your code. Note that this does not mean posting your entire project. Instead, start over with a blank sketch and only create the most basic code required to show the problem. Don't include any of your movie logic, and hardcode as much as possible. We should be able to copy and paste your code onto our own machines to run it and see the problem.
All of that being said, I think I understand what you're asking.
How do I draw the motion of a particular frame without letting what I've have blitted to the screen in the previous frame affect what will be drawn for the next frame. My only way of getting my 10 by 10 pixel box is by using the get() function which gets pixels that are already drawn to the screen.
Separate your program into a view and a model. Right now you're using the screen (the view) to store all of your information, which is going to cause you headaches. Instead, store the state of your program into a set of variables (the model). For you, this might just be a bunch of PVector instances.
Let's say I have an ArrayList<PVector> that holds the current position of all of my vectors:
ArrayList<PVector> currentPositions = new ArrayList<PVector>();
void setup() {
size(500, 500);
for (int i = 0; i < 100; i++) {
currentPositions.add(new PVector(random(width), random(height)));
}
}
void draw(){
background(0);
for(PVector vector : currentPositions){
ellipse(vector.x, vector.y, 10, 10);
}
}
Notice that I'm just hardcoding their positions to be random. This is what your MCVE should do as well. And then in the draw() function, I'm simply drawing each vector. This is like drawing a single frame for you.
Now that we have that, we can create a nextFrame() function that moves the vectors based on the ArrayList (our model) and not what's drawn on the screen!
void nextFrame(){
for(PVector vector : currentPositions){
vector.x += random(-2, 2);
vector.y += random(-2, 2);
}
}
Again, I'm just hardcoding a random movement, but you would be reading these from your file. Then we just call the nextFrame() function as the last line in the draw() function:
If you're still having trouble, I highly recommend posting an MCVE similar to mine and posting a new question. Good luck.
I have a number of shapes in my kinetc layer. If I drag and drop a shape somewhere else in the layer, how to determine the dropped shape touches another shape or not?
The thing you need to do is create a mathematical representation of the shapes you have. For most simple collision detection, you can use bounding-boxes.
Basically, if you have a circle, you can create a representation of it as being bounded in a box.
Then if you have a square, you can check if the bounding box of the square (which is the square itself) is intersecting with the bounding box of the circle.
I wrote an answer to this a while ago: HTML5 / kineticJS getIntersection function implementation
function checkCollide(pointX, pointY, objectx, objecty, objectw, objecth) { // pointX, pointY belong to one rectangle, while the object variables belong to another rectangle
var oTop = objecty;
var oLeft = objectx;
var oRight = objectx+objectw;
var oBottom = objecty+objecth;
if(pointX > oLeft && pointX < oRight){
if(pointY > oTop && pointY < oBottom ){
return 1;
}
}
else
return 0;
};
used like this:
var children = layer.getChildren();
for( var i=0; i<children.length; i++){ // for each single shape
for( var j=0; j<children.length; j++){ //check each other shape
if(i != j){ //skip if shape is the same
if(checkCollide(children[i].getX(), children[i].getY(), children[j].getX(), children[j].getY(), children[j].getWidth(), children[j].getHeight()))
alert('top left corner collided');
}
}
}
This works great if the shape you have is a rectangle of some sort, but not that great if you have two circles, as they have a radius. So this suffices for a quick check of collision, next you need another function which will check collisions more precisely.
You can also try using kineticjs with box2d, there are a bunch of tutorials out there on the topic.
In my (Minecraft-like) 3D voxel world, I want to smooth the shapes for more natural visuals. Let's look at this example in 2D first.
Left is how the world looks without any smoothing. The terrain data is binary and each voxel is rendered as a unit size cube.
In the center you can see a naive circular smoothing. It only takes the four directly adjacent blocks into account. It is still not very natural looking. Moreover, I'd like to have flat 45-degree slopes emerge.
On the right you can see a smoothing algorithm I came up with. It takes the eight direct and diagonal neighbors into account in order to come up with the shape of a block. I have the C++ code online. Here is the code that comes up with the control points that the bezier curve is drawn along.
#include <iostream>
using namespace std;
using namespace glm;
list<list<dvec2>> Points::find(ivec2 block)
{
// Control points
list<list<ivec2>> lines;
list<ivec2> *line = nullptr;
// Fetch blocks, neighbours start top left and count
// around the center block clock wise
int center = m_blocks->get(block);
int neighs[8];
for (int i = 0; i < 8; i++) {
auto coord = blockFromIndex(i);
neighs[i] = m_blocks->get(block + coord);
}
// Iterate over neighbour blocks
for (int i = 0; i < 8; i++) {
int current = neighs[i];
int next = neighs[(i + 1) % 8];
bool is_side = (((i + 1) % 2) == 0);
bool is_corner = (((i + 1) % 2) == 1);
if (line) {
// Border between air and ground needs a line
if (current != center) {
// Sides are cool, but corners get skipped when they don't
// stop a line
if (is_side || next == center)
line->push_back(blockFromIndex(i));
} else if (center || is_side || next == center) {
// Stop line since we found an end of the border. Always
// stop for ground blocks here, since they connect over
// corners so there must be open docking sites
line = nullptr;
}
} else {
// Start a new line for the border between air and ground that
// just appeared. However, corners get skipped if they don't
// end a line.
if (current != center) {
lines.emplace_back();
line = &lines.back();
line->push_back(blockFromIndex(i));
}
}
}
// Merge last line with first if touching. Only close around a differing corner for air
// blocks.
if (neighs[7] != center && (neighs[0] != center || (!center && neighs[1] != center))) {
// Skip first corner if enclosed
if (neighs[0] != center && neighs[1] != center)
lines.front().pop_front();
if (lines.size() == 1) {
// Close circle
auto first_point = lines.front().front();
lines.front().push_back(first_point);
} else {
// Insert last line into first one
lines.front().insert(lines.front().begin(), line->begin(), line->end());
lines.pop_back();
}
}
// Discard lines with too few points
auto i = lines.begin();
while (i != lines.end()) {
if (i->size() < 2)
lines.erase(i++);
else
++i;
}
// Convert to concrete points for output
list<list<dvec2>> points;
for (auto &line : lines) {
points.emplace_back();
for (auto &neighbour : line)
points.back().push_back(pointTowards(neighbour));
}
return points;
}
glm::ivec2 Points::blockFromIndex(int i)
{
// Returns first positive representant, we need this so that the
// conditions below "wrap around"
auto modulo = [](int i, int n) { return (i % n + n) % n; };
ivec2 block(0, 0);
// For two indices, zero is right so skip
if (modulo(i - 1, 4))
// The others are either 1 or -1
block.x = modulo(i - 1, 8) / 4 ? -1 : 1;
// Other axis is same sequence but shifted
if (modulo(i - 3, 4))
block.y = modulo(i - 3, 8) / 4 ? -1 : 1;
return block;
}
dvec2 Points::pointTowards(ivec2 neighbour)
{
dvec2 point;
point.x = static_cast<double>(neighbour.x);
point.y = static_cast<double>(neighbour.y);
// Convert from neighbour space into
// drawing space of the block
point *= 0.5;
point += dvec2(.5);
return point;
}
However, this is still in 2D. How to translate this algorithm into three dimensions?
You should probably have a look at the marching cubes algorithm and work from there. You can easily control the smoothness of the resulting blob:
Imagine that each voxel defines a field, with a high density at it's center, slowly fading to nothing as you move away from the center. For example, you could use a function that is 1 inside a voxel and goes to 0 two voxels away. No matter what exact function you choose, make sure that it's only non-zero inside a limited (preferrably small) area.
For each point, sum the densities of all fields.
Use the marching cubes algorithm on the sum of those fields
Use a high resolution mesh for the algorithm
In order to change the look/smoothness you change the density function and the threshold of the marching cubes algorithm. A possible extension to marching cubes to create smoother meshes is the following idea: Imagine that you encounter two points on an edge of a cube, where one point lies inside your volume (above a threshold) and the other outside (under the threshold). In this case many marching cubes algorithms place the boundary exactly at the middle of the edge. One can calculate the exact boundary point - this gets rid of aliasing.
Also I would recommend that you run a mesh simplification algorithm after that. Using marching cubes results in meshes with many unnecessary triangles.
As an alternative to my answer above: You could also use NURBS or any algorithm for subdivision surfaces. Especially the subdivision surfaces algorithms are spezialized to smooth meshes. Depending on the algorithm and it's configuration you will get smoother versions of your original mesh with
the same volume
the same surface
the same silhouette
and so on.
Use 3D implementations for Biezer curves known as Biezer surfaces or use the B-Spline Surface algorithms explained:
here
or
here