I need to draw a cube in processing without the use of built-in functions. All I could find was using the box() function then rotating the image to see it in 3D.
How can I achieve this?
An alternative is using createShape(BOX) instead:
PShape cube;
void setup(){
size(600, 600, P3D);
strokeWeight(9);
cube = createShape(BOX, 150);
}
void draw(){
background(255);
lights();
translate(width * 0.5, height * 0.5, 0);
rotateY(map(mouseX, 0, width, -PI, PI));
rotateX(map(mouseY, 0, height, PI, -PI));
shape(cube);
}
If this is for a homework/assignment it might not be enough (and you should state that in the question, and you should also post a code snippet of your attempt with a description of what didn't work). You'll need to draw the box from scratch. One easy way to think about is to draw the 8 cube vertices on pen and paper with 0,0,0 at the centre, then keeping in mind where -x/+x, -y/+y, -z/+z axes point, mark each vertex, for example:
( x, y, z)
(-1,-1,-1)
(+1,-1,-1)
(+1,+1,-1)
(-1,+1,-1)
should be the back face.
Repeat the process for the rest of the faces.
In Processing you'd use beginShape(QUADS) since you're drawing a cube and it's faces are quads, vertex(x, y, z) to specify each point/vertex and finally endShape() to complete the shape (and "join the dots"):
void setup(){
size(600, 600, P3D);
strokeWeight(9);
}
void draw(){
background(255);
lights();
translate(width * 0.5, height * 0.5, 0);
rotateY(map(mouseX, 0, width, -PI, PI));
rotateX(map(mouseY, 0, height, PI, -PI));
cubeFaces(150);
}
void cubeFaces(float size){
// half size: keep the pivot at the center of the mesh
float s = size * 0.5;
beginShape(QUADS);
// back (-z)
vertex(-s, -s, -s);
vertex(+s, -s, -s);
vertex(+s, +s, -s);
vertex(-s, +s, -s);
// front (+z)
vertex(-s, -s, +s);
vertex(+s, -s, +s);
vertex(+s, +s, +s);
vertex(-s, +s, +s);
// top (-y)
vertex(-s, -s, +s);
vertex(-s, -s, -s);
vertex(+s, -s, -s);
vertex(+s, -s, +s);
// bottom (+y)
vertex(+s, +s, +s);
vertex(+s, +s, -s);
vertex(-s, +s, -s);
vertex(-s, +s, +s);
// left (-x)
vertex(-s, -s, +s);
vertex(-s, -s, -s);
vertex(-s, +s, -s);
vertex(-s, +s, +s);
// right (+x)
vertex(+s, -s, +s);
vertex(+s, -s, -s);
vertex(+s, +s, -s);
vertex(+s, +s, +s);
endShape();
}
PShape is useful because you can cache the geometry using a very similar beginShape()/endShape(), but rather than recreating the shape every frame you can simply render the predefined geometry:
PShape cube;
void setup(){
size(600, 600, P3D);
strokeWeight(9);
cube = getCube(150);
}
void draw(){
background(255);
lights();
translate(width * 0.5, height * 0.5, 0);
rotateY(map(mouseX, 0, width, -PI, PI));
rotateX(map(mouseY, 0, height, PI, -PI));
shape(cube);
}
void cubeFaces(float size){
// half size: keep the pivot at the center of the mesh
float s = size * 0.5;
beginShape(QUADS);
// back (-z)
vertex(-s, -s, -s);
vertex(+s, -s, -s);
vertex(+s, +s, -s);
vertex(-s, +s, -s);
// front (+z)
vertex(-s, -s, +s);
vertex(+s, -s, +s);
vertex(+s, +s, +s);
vertex(-s, +s, +s);
// top (-y)
vertex(-s, -s, +s);
vertex(-s, -s, -s);
vertex(+s, -s, -s);
vertex(+s, -s, +s);
// bottom (+y)
vertex(+s, +s, +s);
vertex(+s, +s, -s);
vertex(-s, +s, -s);
vertex(-s, +s, +s);
// left (-x)
vertex(-s, -s, +s);
vertex(-s, -s, -s);
vertex(-s, +s, -s);
vertex(-s, +s, +s);
// right (+x)
vertex(+s, -s, +s);
vertex(+s, -s, -s);
vertex(+s, +s, -s);
vertex(+s, +s, +s);
endShape();
}
PShape getCube(float size){
PShape cube = createShape();
// half size: keep the pivot at the center of the mesh
float s = size * 0.5;
cube.beginShape(QUADS);
// back (-z)
cube.vertex(-s, -s, -s);
cube.vertex(+s, -s, -s);
cube.vertex(+s, +s, -s);
cube.vertex(-s, +s, -s);
// front (+z)
cube.vertex(-s, -s, +s);
cube.vertex(+s, -s, +s);
cube.vertex(+s, +s, +s);
cube.vertex(-s, +s, +s);
// top (-y)
cube.vertex(-s, -s, +s);
cube.vertex(-s, -s, -s);
cube.vertex(+s, -s, -s);
cube.vertex(+s, -s, +s);
// bottom (+y)
cube.vertex(+s, +s, +s);
cube.vertex(+s, +s, -s);
cube.vertex(-s, +s, -s);
cube.vertex(-s, +s, +s);
// left (-x)
cube.vertex(-s, -s, +s);
cube.vertex(-s, -s, -s);
cube.vertex(-s, +s, -s);
cube.vertex(-s, +s, +s);
// right (+x)
cube.vertex(+s, -s, +s);
cube.vertex(+s, -s, -s);
cube.vertex(+s, +s, -s);
cube.vertex(+s, +s, +s);
cube.endShape();
return cube;
}
Bonus points, there's yet another way you can draw a cube: as a special case of a cylinder with 4 subdivions where the radius is half the height.
Processing ships with an example you can tweak in Processing > Examples >
Topics > Geometry > Vertices.
Here's a modified version of the example rendering a cube:
/**
* Vertices
* by Simon Greenwold.
*
* Draw a cylinder centered on the y-axis, going down
* from y=0 to y=height. The radius at the top can be
* different from the radius at the bottom, and the
* number of sides drawn is variable.
*/
void setup() {
size(640, 360, P3D);
}
void draw() {
background(0);
lights();
translate(width / 2, height / 2);
rotateY(map(mouseX, 0, width, 0, PI));
rotateZ(map(mouseY, 0, height, 0, -PI));
noStroke();
fill(255, 255, 255);
translate(0, -40, 0);
//drawCylinder(10, 180, 200, 16); // Draw a mix between a cylinder and a cone
//drawCylinder(70, 70, 120, 64); // Draw a cylinder
//drawCylinder(0, 180, 200, 4); // Draw a pyramid
// Draw a cube
drawCylinder(50, 50, 75, 4);
}
void drawCylinder(float topRadius, float bottomRadius, float tall, int sides) {
float angle = 0;
float angleIncrement = TWO_PI / sides;
beginShape(QUAD_STRIP);
for (int i = 0; i < sides + 1; ++i) {
vertex(topRadius*cos(angle), 0, topRadius*sin(angle));
vertex(bottomRadius*cos(angle), tall, bottomRadius*sin(angle));
angle += angleIncrement;
}
endShape();
// If it is not a cone, draw the circular top cap
if (topRadius != 0) {
angle = 0;
beginShape(TRIANGLE_FAN);
// Center point
vertex(0, 0, 0);
for (int i = 0; i < sides + 1; i++) {
vertex(topRadius * cos(angle), 0, topRadius * sin(angle));
angle += angleIncrement;
}
endShape();
}
// If it is not a cone, draw the circular bottom cap
if (bottomRadius != 0) {
angle = 0;
beginShape(TRIANGLE_FAN);
// Center point
vertex(0, tall, 0);
for (int i = 0; i < sides + 1; i++) {
vertex(bottomRadius * cos(angle), tall, bottomRadius * sin(angle));
angle += angleIncrement;
}
endShape();
}
}
(You can find my explanation of the polar to cartesian formula used above in this answer)
The above is very similar to the old school OpenGL immediate mode drawing. If this is for a more recent CS oriented course for OpenGL one, you might need to use PGL and a vertex array buffer, slightly different calls. The Cube OpenGL Tutorial cover that nicely. Even though it's c++, you'll recognize functions such as genBuffers(), glBindBuffer(), glBufferData() in Processing's PGL class (
Processing > Examples > Demos > Graphics > LowLevelGL* examples are great starting points and align nicely with the triangle OpenGL tutorial)
Update:
There's yet another example that can easily be tweaked to draw a cube:
Processing > Examples > Demos > Graphics > Wiggling.
Here's a tweaked version that draws the cube without the wiggling nor the holes in the faces:
PShape cube;
float cubeSize = 320;
void setup() {
size(1024, 768, P3D);
createCube();
}
void draw() {
background(0);
translate(width/2, height/2);
rotateX(frameCount * 0.01f);
rotateY(frameCount * 0.01f);
shape(cube);
if (frameCount % 60 == 0) println(frameRate);
}
public void keyPressed() {
if (key == '1') {
cube.setStrokeWeight(1);
} else if (key == '2') {
cube.setStrokeWeight(5);
} else if (key == '3') {
cube.setStrokeWeight(10);
}
}
void createCube() {
cube = createShape(GROUP);
PShape face;
// Create all faces at front position
for (int i = 0; i < 6; i++) {
face = createShape();
createFace(face);
cube.addChild(face);
}
// Rotate all the faces to their positions
// Front face - already correct
face = cube.getChild(0);
// Back face
face = cube.getChild(1);
face.rotateY(radians(180));
// Right face
face = cube.getChild(2);
face.rotateY(radians(90));
// Left face
face = cube.getChild(3);
face.rotateY(radians(-90));
// Top face
face = cube.getChild(4);
face.rotateX(radians(90));
// Bottom face
face = cube.getChild(5);
face.rotateX(radians(-90));
}
void createFace(PShape face) {
face.beginShape(POLYGON);
face.stroke(255, 0, 0);
face.fill(255);
// Draw main shape Clockwise
face.vertex(-cubeSize/2, -cubeSize/2, +cubeSize/2);
face.vertex(+cubeSize/2, -cubeSize/2, +cubeSize/2);
face.vertex(+cubeSize/2, +cubeSize/2, +cubeSize/2);
face.vertex(-cubeSize / 2, +cubeSize / 2, +cubeSize / 2);
face.endShape(CLOSE);
}
Related
I want to detect separate shapes as random-generated lines split the canvas. I saved line intersection points in separate arrays for x and y positions (same order), but don't know how to connect
points that complete multiple pieces of shapes .
Is there any way to detect nearby points to close a minimal possible shape whether it be a triangle, rectangle, or polygon (e.g., by using beginShape and endShape)?
If 1) is too complicated, is there any method to select 3 or more random points from an array?
Here's a sample image that has 4 lines splitting the canvas with their intersection points marked in red. I also saved the top and bottom points (marked in black) of each random-generated line, plus the four corners of the canvas in the same arrays for x and y positions separately (px, py).
Multiple lines split the canvas.
How to get shapes split by lines in Processing?
I was able to get all the intersection points, but having a problem with connecting them into separate shapes. Here's the Processing code that I am working on:
//Run in Processing.
//Press r to refresh.
//Top and bottom points are added to px and py when refreshed (filled in black).
//Intersection points are added to px and py when detected (filled in red).
int l = 4; //set number of lines
float[] r1 = new float[l];
float[] r2 = new float[l];
float[] px = {}; //array to save x positions of all possible points
float[] py = {}; //array to save y positions of all possible points
boolean added = false;
void setup(){
size(800, 800);
background(255);
refresh();
}
void draw(){
background(255);
stroke(0, 150, 255, 150);
strokeWeight(1);
for(int i=0; i < r1.length; i++){
for(int j=0; j < r1.length; j++){
if(i>j){
boolean hit = lineLine(r1[i], 0, r2[i], height, r1[j], 0, r2[j], height);
if (hit) stroke(255, 150, 0, 150);
else stroke(0, 150, 255, 150);
}
line(r1[i], 0, r2[i], height);
}
}
added = true;
print(px.length);
}
//source: http://jeffreythompson.org/collision-detection/line-line.php
boolean lineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
// calculate the distance to intersection point
float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
// if uA and uB are between 0-1, lines are colliding
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
// optionally, draw a circle where the lines meet
float intersectionX = x1 + (uA * (x2-x1));
float intersectionY = y1 + (uA * (y2-y1));
fill(255,0,0);
noStroke();
ellipse(intersectionX,intersectionY, 20,20);
if(added==false){
px = append(px, intersectionX);
py = append(py, intersectionY);
}
return true;
}
return false;
}
void refresh(){
added = false;
px = new float[0];
py = new float[0];
r1 = new float[l];
r2 = new float[l];
px = append(px, 0);
py = append(py, 0);
px = append(px, 0);
py = append(py, height);
px = append(px, width);
py = append(py, 0);
px = append(px, width);
py = append(py, height);
for(int i=0; i< r1.length; i++){
r1[i] = random(800);
}
for(int i=0; i< r2.length; i++){
r2[i] = random(800);
}
for(int i=0; i < r1.length; i++){
stroke(0);
line(r1[i], 0, r2[i], height);
px = append(px, r1[i]);
py = append(py, 0);
px = append(px, r2[i]);
py = append(py, height);
}
}
void keyReleased() {
if (key == 'r') refresh();
}
If you want to draw a shape made of the intersection points only you're on the right track with beginShape()/endShape().
Currently it looks like you're placing all the points in px, py: the intersection points and also the points defining the lines used to compute the intersections in the first place.
You might want to separate the two, for example a couply of arrays for points defining lines only and another pair of x,y arrays for the intersection points only. You'd only need to iterated through the intersected coordinates to place vertex(x, y) calls inbetween beginShape()/endShape(). Here's a modified version of you code to illustrate the idea:
//Run in Processing.
//Press r to refresh.
//Top and bottom points are added to px and py when refreshed (filled in black).
//Intersection points are added to px and py when detected (filled in red).
int l = 4; //set number of lines
float[] r1 = new float[l];
float[] r2 = new float[l];
float[] px = {}; //array to save x positions of all possible points
float[] py = {}; //array to save y positions of all possible points
float[] ipx = {}; // array to save x for intersections only
float[] ipy = {}; // array to save y for intersections only
boolean added = false;
void setup(){
size(800, 800);
background(255);
refresh();
}
void draw(){
background(255);
stroke(0, 150, 255, 150);
strokeWeight(1);
for(int i=0; i < r1.length; i++){
for(int j=0; j < r1.length; j++){
if(i>j){
boolean hit = lineLine(r1[i], 0, r2[i], height, r1[j], 0, r2[j], height);
if (hit) stroke(255, 150, 0, 150);
else stroke(0, 150, 255, 150);
}
line(r1[i], 0, r2[i], height);
}
}
added = true;
// draw intersections
beginShape();
for(int i = 0 ; i < ipx.length; i++){
vertex(ipx[i], ipy[i]);
}
endShape();
//print(px.length);
//println(px.length, py.length);
}
//source: http://jeffreythompson.org/collision-detection/line-line.php
boolean lineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
// calculate the distance to intersection point
float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
// if uA and uB are between 0-1, lines are colliding
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
// optionally, draw a circle where the lines meet
float intersectionX = x1 + (uA * (x2-x1));
float intersectionY = y1 + (uA * (y2-y1));
fill(255,0,0);
noStroke();
ellipse(intersectionX,intersectionY, 20,20);
if(added==false){
px = append(px, intersectionX);
py = append(py, intersectionY);
// store intersections
ipx = append(ipx, intersectionX);
ipy = append(ipy, intersectionY);
}
return true;
}
return false;
}
void refresh(){
added = false;
px = new float[0];
py = new float[0];
ipx = new float[0];
ipy = new float[0];
r1 = new float[l];
r2 = new float[l];
px = append(px, 0);
py = append(py, 0);
px = append(px, 0);
py = append(py, height);
px = append(px, width);
py = append(py, 0);
px = append(px, width);
py = append(py, height);
for(int i=0; i< r1.length; i++){
r1[i] = random(800);
}
for(int i=0; i< r2.length; i++){
r2[i] = random(800);
}
for(int i=0; i < r1.length; i++){
stroke(0);
line(r1[i], 0, r2[i], height);
px = append(px, r1[i]);
py = append(py, 0);
px = append(px, r2[i]);
py = append(py, height);
}
}
void keyReleased() {
if (key == 'r') refresh();
}
Bare in mind this simlpy draws the points in the order in which the intersections were computed. On a good day you'll get something like this:
It doesn't exclude the possiblity of polygons with the wrong vertex order (winding):
and you might be get convave polygons too.
If you only need the outer 'shell' of these intersection points you might need something like a convex hull algorithm
One option to at least visually split shapes might to use beginShape(TRIANGLES); with endShape(CLOSE); which should iterate through points and draw a triangle for every coordinate triplate, however given random points and number of interesections you might end up with a missing triangle or two (e.g. 6 points = 2 triangles, 7 points = 2 triangles and 1 point with no missing pairs)
The only other note I have is around syntax: arrays are ok to get started with but you might want to look into ArrayList and PVector. This would allow you to use a single dynamic array of PVector instances which have x, y properties.
Update
Overall the code can be simplified. If we take out the line intersection related code we can get away with something like:
int l = 4; //set number of random lines
float[] r1 = new float[l]; // random x top
float[] r2 = new float[l]; // random x bottom
void setup() {
size(800, 800);
strokeWeight(3);
stroke(0, 150, 255, 150);
refresh();
}
void draw() {
background(255);
// random lines
for (int i=0; i < r1.length; i++) {
line(r1[i], 0, r2[i], height);
}
// borders
line(0, 0, width, 0);
line(width, 0, width - 1, height - 1);
line(0, height - 1, width - 1, height - 1);
line(0, 0, 0, height - 1);
}
void refresh() {
r1 = new float[l];
r2 = new float[l];
for (int i=0; i< r1.length; i++) {
r1[i] = random(800);
r2[i] = random(800);
}
}
void keyReleased() {
if (key == 'r') refresh();
}
If we were to use a basic Line class and make use of PVector and ArrayList we could rewrite the above as:
int numRandomLines = 4;
ArrayList<PVector> points = new ArrayList<PVector>();
void setup() {
size(800, 800);
stroke(0, 150, 255, 150);
strokeWeight(3);
refresh();
}
void refresh(){
// remove previous points
points.clear();
//add borders
points.add(new PVector(0, 0)); points.add(new PVector(width, 0));
points.add(new PVector(width, 0));points.add(new PVector(width - 1, height - 1));
points.add(new PVector(0, height - 1));points.add(new PVector(width - 1, height - 1));
points.add(new PVector(0, 0)); points.add(new PVector(0, height - 1));
// add random lines
for (int i=0; i< numRandomLines; i++) {
points.add(new PVector(random(800), 0)); points.add(new PVector(random(800), height));
}
}
void draw(){
background(255);
beginShape(LINES);
for(PVector point : points) vertex(point.x, point.y);
endShape();
}
void keyReleased() {
if (key == 'r') refresh();
}
and grouping a pair of points (PVector) into a Line class:
int numRandomLines = 4;
ArrayList<Line> lines = new ArrayList<Line>();
void setup() {
size(800, 800);
stroke(0, 150, 255, 150);
strokeWeight(3);
refresh();
}
void refresh(){
// remove previous points
lines.clear();
//add borders
lines.add(new Line(new PVector(0, 0), new PVector(width, 0)));
lines.add(new Line(new PVector(width, 0), new PVector(width - 1, height - 1)));
lines.add(new Line(new PVector(0, height - 1), new PVector(width - 1, height - 1)));
lines.add(new Line(new PVector(0, 0), new PVector(0, height - 1)));
// add random lines
for (int i=0; i< numRandomLines; i++) {
lines.add(new Line(new PVector(random(800), 0), new PVector(random(800), height)));
}
}
void draw(){
background(255);
for(Line line : lines) line.draw();
}
void keyReleased() {
if (key == 'r') refresh();
}
class Line{
PVector start;
PVector end;
Line(PVector start, PVector end){
this.start = start;
this.end = end;
}
void draw(){
line(start.x, start.y, end.x, end.y);
}
}
At this stage to get the individual shapes as your diagram describes, we could cheat and use a computer vision library like OpenCV. This is if course overkill (as we'd get() a PImage copy of the drawing, convert that to an OpenCV image) then simply use findContours() to get each shape/contour.
Going back to the original approach, the line to line intersection function could be integrated into the Line class:
int numRandomLines = 4;
ArrayList<Line> lines = new ArrayList<Line>();
ArrayList<PVector> intersections = new ArrayList<PVector>();
void setup() {
size(800, 800);
strokeWeight(3);
refresh();
}
void refresh(){
// remove previous points
lines.clear();
intersections.clear();
//add borders
lines.add(new Line(new PVector(0, 0), new PVector(width, 0)));
lines.add(new Line(new PVector(width, 0), new PVector(width - 1, height - 1)));
lines.add(new Line(new PVector(0, height - 1), new PVector(width - 1, height - 1)));
lines.add(new Line(new PVector(0, 0), new PVector(0, height - 1)));
// add random lines
for (int i=0; i< numRandomLines; i++) {
lines.add(new Line(new PVector(random(800), 0), new PVector(random(800), height)));
}
// compute intersections
int numLines = lines.size();
// when looping only check if lineA intersects lineB but not also if lineB intersects lineA (redundant)
for (int i = 0; i < numLines - 1; i++){
Line lineA = lines.get(i);
for (int j = i + 1; j < numLines; j++){
Line lineB = lines.get(j);
// check intersection
PVector intersection = lineA.intersect(lineB);
// if there is one, append the intersection point to the list
if(intersection != null){
intersections.add(intersection);
}
}
}
}
void draw(){
background(255);
stroke(0, 150, 255, 150);
// draw lines
for(Line line : lines) line.draw();
stroke(255, 0, 0, 150);
// draw intersections
for(PVector intersection : intersections) ellipse(intersection.x, intersection.y, 9, 9);
}
void keyReleased() {
if (key == 'r') refresh();
}
class Line{
PVector start;
PVector end;
Line(PVector start, PVector end){
this.start = start;
this.end = end;
}
void draw(){
line(start.x, start.y, end.x, end.y);
}
//source: http://jeffreythompson.org/collision-detection/line-line.php
//boolean lineLine(float this.start.x, float this.start.y, float this.end.x, float this.end.y,
//float other.start.x, float other.start.y, float other.end.x, float other.end.y) {
PVector intersect(Line other) {
// calculate the distance to intersection point
float uA = ((other.end.x-other.start.x)*(this.start.y-other.start.y) - (other.end.y-other.start.y)*(this.start.x-other.start.x)) / ((other.end.y-other.start.y)*(this.end.x-this.start.x) - (other.end.x-other.start.x)*(this.end.y-this.start.y));
float uB = ((this.end.x-this.start.x)*(this.start.y-other.start.y) - (this.end.y-this.start.y)*(this.start.x-other.start.x)) / ((other.end.y-other.start.y)*(this.end.x-this.start.x) - (other.end.x-other.start.x)*(this.end.y-this.start.y));
// if uA and uB are between 0-1, lines are colliding
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
// optionally, draw a circle where the lines meet
float intersectionX = this.start.x + (uA * (this.end.x-this.start.x));
float intersectionY = this.start.y + (uA * (this.end.y-this.start.y));
return new PVector(intersectionX, intersectionY);
}
return null;
}
}
The next step would be a more complex algorithm to sort the points based on x, y position (e.g. top to bottom , left to right), iterate though points comparing the first to the rest by distance and angle and trying to work out if consecutive points with minimal distance and angle changes connect.
Having a quick look online I can see such algorithms for example:
Polygon Detection from a Set of Lines
Bentley Ottman algorithm (one of the algorithms mentioned in the paper above) is actually implemented in CGAL. (While there are CGAL Java bindings building these and either interfacing or making a wrapper for Processing isn't trivial).
I can see your code isn't javascript but since you didn't specify a language I assume you just want a method and can convert to your language.
The way I handled this was to assign each line a line number. If I can identify 2 adjacent points on one line then I will know if the third point exist by checking if there is a point at the crossing of the lines they are not sharing.
Example:
There's 3 lines (line 1, 2, 3)
I have an intersection point between lines 3 & 1 now I walk down line 3 for an adjacent point. I find one and its intersection is 3 & 2. Well the only way I could have a triangle is by lines 1 & 2 crossing somewhere. So we can programmatically check that.
Keep in mind that I never actually use and angles for this. I do calculate them in the functions but decided not to use them as I went with the method explained above. I have colored the triangles using an alpha value of 0.1 so you can see where there is overlap.
This is only check triangles
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 400;
let lines = []; //holds each line
let points = []; //all intersection point are pushed here [{x: num, y: num}, {x: num, y: num},...]
let sortedPts = []; //all points sorted bu first number are pushed here in 2d array.
let lineNum = 15;
class Lines {
constructor(num) {
this.x = Math.round(Math.random() * canvas.width);
this.x2 = Math.round(Math.random() * canvas.width);
this.pt1 = {
x: this.x,
y: 0
};
this.pt2 = {
x: this.x2,
y: canvas.height
};
this.num = num;
this.rads = Math.atan2(this.pt2.y - this.pt1.y, this.pt2.x - this.pt1.x);
this.angle = this.rads * (180 / Math.PI);
}
draw() {
ctx.beginPath();
ctx.moveTo(this.pt1.x, this.pt1.y);
ctx.lineTo(this.pt2.x, this.pt2.y);
ctx.stroke();
}
}
//creates the lines. I also use this function to prepare the 2d array by pushing an empty array for each line into sortedPts.
function createLines() {
for (let i = 0; i < lineNum; i++) {
lines.push(new Lines(i + 1));
sortedPts.push([])
}
}
createLines();
//Visually draws lines on screen
function drawLines() {
for (let i = 0; i < lines.length; i++) {
lines[i].draw();
}
}
drawLines();
//intersecting formula
function lineSegmentsIntersect(line1, line2) {
let a_dx = line1.pt2.x - line1.pt1.x;
let a_dy = line1.pt2.y - line1.pt1.y;
let b_dx = line2.pt2.x - line2.pt1.x;
let b_dy = line2.pt2.y - line2.pt1.y;
let s =
(-a_dy * (line1.pt1.x - line2.pt1.x) + a_dx * (line1.pt1.y - line2.pt1.y)) /
(-b_dx * a_dy + a_dx * b_dy);
let t =
(+b_dx * (line1.pt1.y - line2.pt1.y) - b_dy * (line1.pt1.x - line2.pt1.x)) /
(-b_dx * a_dy + a_dx * b_dy);
if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
//this is where we create our array but we also add the line number of where each point intersects. I also add the angle but have not used it throughout the rest of this...yet.
points.push({
x: Math.round(line1.pt1.x + t * (line1.pt2.x - line1.pt1.x)),
y: Math.round(line1.pt1.y + t * (line1.pt2.y - line1.pt1.y)),
num: {
first: line1.num,
second: line2.num
},
angle: {
a1: line1.angle,
a2: line2.angle
}
});
}
}
//just checks each line against the others by passing to lineSegmentsIntersect() function
function callIntersect() {
for (let i = 0; i < lines.length; i++) {
for (let j = i + 1; j < lines.length; j++) {
lineSegmentsIntersect(lines[i], lines[j]);
}
}
}
callIntersect();
function drawPoints() {
//just draws the black points for reference
for (let i = 0; i < points.length; i++) {
ctx.beginPath();
ctx.arc(points[i].x, points[i].y, 2, 0, Math.PI * 2);
ctx.fill();
}
}
drawPoints();
function createSortedArray() {
//Now we take the points array and sort the points by the first number to make using i and j below possible
points.sort((a, b) => a.num.first - b.num.first)
//We push each group of points into an array inside sortedPts creating the 2d array
for (let i = 0; i < lineNum; i++) {
for (let j = 0; j < points.length; j++) {
if (points[j].num.first == (i + 1)) {
sortedPts[i].push(points[j]);
}
}
}
//now sort the 2d arrays by y value. This allows or next check to go in order from point to point per line.
sortedPts.forEach(arr => arr.sort((a, b) => a.y - b.y));
fillTriangles();
}
createSortedArray();
/*
The last step iterates through each point in the original points array
and check to see if either the first or second number matches the second
number of a point in our sortedPts array AND do the first or second number
match the next points in the sortedPtsd array. If so then we must have a
triangle.
Quick breakdown. If we have 3 lines (line 1, 2, 3) and I have a points on lines
2 & 3. I also have another point on lines 2 & 1. Then in order to have a triangle
the last point must be on lines 1 & 3.
That's all this is doing.
*/
function fillTriangles() {
//iterate through each array inside sortedPts array
for (let i = 0; i < sortedPts.length; i++) {
//iterate through all points inside each array of points inside the sortedPts array
for (let j = 0; j < sortedPts[i].length - 1; j++) {
//iterate over the original points and compare
for (let k = 0; k < points.length; k++) {
if (
(points[k].num.first == sortedPts[i][j].num.second ||
points[k].num.second == sortedPts[i][j].num.second) &&
(points[k].num.first == sortedPts[i][j + 1].num.second ||
points[k].num.second == sortedPts[i][j + 1].num.second)
) {
ctx.fillStyle = "rgba(200, 100, 0, 0.1)";
ctx.beginPath();
ctx.moveTo(sortedPts[i][j].x, sortedPts[i][j].y);
ctx.lineTo(sortedPts[i][j + 1].x, sortedPts[i][j + 1].y);
ctx.lineTo(points[k].x, points[k].y);
ctx.closePath();
ctx.fill();
}
}
}
}
}
<canvas id="canvas"></canvas>
I also think there's a good way to do this with the angles of the crossing lines and am working on something to do it that way. I am hoping I can get it to determine the type of shape based on the number of sides but I don't see that being a quick project.
Your goal is not clear to me. You can connect any arbitrary set of points in any arbitrary order and call it a shape. What are your criteria?
If you want to find the shortest path that connects all the points of a given subset, I suggest looking for travelling salesman problem.
In processing, when you apply a matrix transformation, you can draw on your canvas without worrying of the "true" position of your x y coordinate.
I thought that by the same logic, I could copy a section of the canvas by using ParentApplet.get(x, y, width, height) and that it would automatically shift the x and y, but it does not, it uses the coordinates as raw inputs without applying the matrix stack to it.
So the easiest way I see to deal with the problem would be to manually apply the matrix stack to my x, y, width, height values and using the results as input of get(). But I cannot find such a function, does one exist ?
EDIT : As requested, Here's an example of my problem
So the objective here is to draw a simple shape, copy it and paste it. Without translate, there is no problem:
void settings(){
size(500, 500);
}
void draw() {
background(255);
// Fancy rectangle for visibility
fill(255, 0 ,0);
rect(0, 0, 100, 100);
fill(0, 255, 0);
rect(20, 20, 60, 60);
// copy rectangle and paste it elsewhere
PImage img = get(0, 0, 101, 101);
image(img, 200, 200);
}
Now if I applied a translate matrix before drawing the shape, I wish that I could use the same get() code to copy the exact same drawing:
void settings(){
size(500, 500);
}
void draw() {
background(255);
pushMatrix();
translate(10, 10);
// Fancy rectangle for visibility
fill(255, 0 ,0);
rect(0, 0, 100, 100);
fill(0, 255, 0);
rect(20, 20, 60, 60);
// copy rectangle and paste it elsewhere
PImage img = get(0, 0, 101, 101);
image(img, 200, 200);
popMatrix();
}
But it doesn't work that way, The get(0, 0, ..) doesn't use the current transformation matrix to copy pixels from origin (10, 10):
Can you please provide a few more details.
It is possible to manipulate coordinate systems using pushMatrix()/PopMatrix() and you can go further and manually multiply matrices and vectors.
The part that is confusing is that you're calling get(x,y,width,height) but no showing how you render the PImage section. It's hard to guess the matrix stack you're mentioning. Can you post an example snippet ?
If you render it at the same x,y you call get() with it should render with the same x,y shift:
size(640, 360);
noFill();
strokeWeight(9);
PImage placeholderForPGraphics = loadImage("https://processing.org/examples/moonwalk.jpg");
image(placeholderForPGraphics, 0, 0);
int x = 420;
int y = 120;
int w = 32;
int h = 48;
// visualise region of interest
rect(x, y, w, h);
// grab the section sub PImage
PImage section = placeholderForPGraphics.get(x, y, w, h);
//filter the section to make it really standout
section.filter(THRESHOLD);
// display section at same location
image(section, x, y);
Regarding the matrix stack, you can call getMatrix() which will return a PMatrix2D if you're in 2D mode (otherwise a PMatrix3D). This is a copy of the current matrix stack at the state you've called it (any prior operations will be "baked" into this one).
For example:
PMatrix m = g.getMatrix();
printArray(m.get(new float[]{}));
(g.printMatrix() should be easier to print to console, but you need to call getMatrix() if you need an instance to manipulate)
Where g is your PGraphics instance.
You can then manipulate it as you like:
m.translate(10, 20);
m.rotate(radians(30));
m.scale(1.5);
Remember to call applyMatrix() it when you're done:
g.applyMatrix(m);
Trivial as it may be I hope this modified version of the above example illustrates the idea:
size(640, 360);
noFill();
strokeWeight(9);
// get the current transformation matrix
PMatrix m = g.getMatrix();
// print to console
println("before");
g.printMatrix();
// modify it
m.translate(160, 90);
m.scale(0.5);
// apply it
g.applyMatrix(m);
// print applied matrix
println("after");
g.printMatrix();
PImage placeholderForPGraphics = loadImage("https://processing.org/examples/moonwalk.jpg");
image(placeholderForPGraphics, 0, 0);
int x = 420;
int y = 120;
int w = 32;
int h = 48;
// visualise region of interest
rect(x, y, w, h);
// grab the section sub PImage
PImage section = placeholderForPGraphics.get(x, y, w, h);
//filter the section to make it really standout
section.filter(THRESHOLD);
// display section at same location
image(section, x, y);
Here's another example making a basic into PGraphics using matrix transformations:
void setup(){
size(360, 360);
// draw something manipulating the coordinate system
PGraphics pg = createGraphics(360, 360);
pg.beginDraw();
pg.background(0);
pg.noFill();
pg.stroke(255, 128);
pg.strokeWeight(4.5);
pg.rectMode(CENTER);
pg.translate(180,180);
for(int i = 0 ; i < 72; i++){
pg.rotate(radians(5));
pg.scale(0.95);
//pg.rect(0, 0, 320, 320, 32, 32, 32, 32);
polygon(6, 180, pg);
}
pg.endDraw();
// render PGraphics
image(pg, 0, 0);
}
This is overkill: the same effect could have been drawn much simpler, however the focus in on calling get() and using transformation matrices. Here a modified iteration showing the same principle with get(x,y,w,h), then image(section,x,y):
void setup(){
size(360, 360);
// draw something manipulating the coordinate system
PGraphics pg = createGraphics(360, 360);
pg.beginDraw();
pg.background(0);
pg.noFill();
pg.stroke(255, 128);
pg.strokeWeight(4.5);
pg.rectMode(CENTER);
pg.translate(180,180);
for(int i = 0 ; i < 72; i++){
pg.rotate(radians(5));
pg.scale(0.95);
//pg.rect(0, 0, 320, 320, 32, 32, 32, 32);
polygon(6, 180, pg);
}
pg.endDraw();
// render PGraphics
image(pg, 0, 0);
// take a section of PGraphics instance
int w = 180;
int h = 180;
int x = (pg.width - w) / 2;
int y = (pg.height - h) / 2;
PImage section = pg.get(x, y, w, h);
// filter section to emphasise
section.filter(INVERT);
// render section at sampled location
image(section, x, y);
popMatrix();
}
void polygon(int sides, float radius, PGraphics pg){
float angleIncrement = TWO_PI / sides;
pg.beginShape();
for(int i = 0 ; i <= sides; i++){
float angle = (angleIncrement * i) + HALF_PI;
pg.vertex(cos(angle) * radius, sin(angle) * radius);
}
pg.endShape();
}
Here's a final iteration re-applying the last transformation matrix in an isolated coordinate space (using push/pop matrix calls):
void setup(){
size(360, 360);
// draw something manipulating the coordinate system
PGraphics pg = createGraphics(360, 360);
pg.beginDraw();
pg.background(0);
pg.noFill();
pg.stroke(255, 128);
pg.strokeWeight(4.5);
pg.rectMode(CENTER);
pg.translate(180,180);
for(int i = 0 ; i < 72; i++){
pg.rotate(radians(5));
pg.scale(0.95);
//pg.rect(0, 0, 320, 320, 32, 32, 32, 32);
polygon(6, 180, pg);
}
pg.endDraw();
// render PGraphics
image(pg, 0, 0);
// take a section of PGraphics instance
int w = 180;
int h = 180;
int x = (pg.width - w) / 2;
int y = (pg.height - h) / 2;
PImage section = pg.get(x, y, w, h);
// filter section to emphasise
section.filter(INVERT);
// print last state of the transformation matrix
pg.printMatrix();
// get the last matrix state
PMatrix m = pg.getMatrix();
// isolate coordinate space
pushMatrix();
//apply last PGraphics matrix
applyMatrix(m);
// render section at sampled location
image(section, x, y);
popMatrix();
save("state3.png");
}
void polygon(int sides, float radius, PGraphics pg){
float angleIncrement = TWO_PI / sides;
pg.beginShape();
for(int i = 0 ; i <= sides; i++){
float angle = (angleIncrement * i) + HALF_PI;
pg.vertex(cos(angle) * radius, sin(angle) * radius);
}
pg.endShape();
}
This is an extreme example, as 0.95 downscale is applied 72 times, hence a very small image is rendered. Also notice the rotation is incremented.
Update Based on your update snippet it seems the confusion is around pushMatrix() and get().
In your scenario, pushMatrix()/translate() will offset the local coordinate sytem: that is where elements are drawn.
get() is called globally and uses absolute coordinates.
If you're only using translation, you can simply store the translation coordinates and re-use them to sample from the same location:
int sampleX = 10;
int sampleY = 10;
void settings(){
size(500, 500);
}
void draw() {
background(255);
pushMatrix();
translate(sampleX, sampleY);
// Fancy rectangle for visibility
fill(255, 0 ,0);
rect(0, 0, 100, 100);
fill(0, 255, 0);
rect(20, 20, 60, 60);
// copy rectangle and paste it elsewhere
PImage img = get(sampleX, sampleY, 101, 101);
image(img, 200, 200);
popMatrix();
}
Update
Here are a couple more examples on how to compute, rather than hard code the translation value:
void settings(){
size(500, 500);
}
void setup() {
background(255);
pushMatrix();
translate(10, 10);
// Fancy rectangle for visibility
fill(255, 0 ,0);
rect(0, 0, 100, 100);
fill(0, 255, 0);
rect(20, 20, 60, 60);
// local to global coordinate conversion using PMatrix
// g is the global PGraphics instance every PApplet (sketch) uses
PMatrix m = g.getMatrix();
printArray(m.get(null));
// the point in local coordinate system
PVector local = new PVector(0,0);
// multiply local point by transformation matrix to get global point
// we pass in null to get a new PVector instance: you can make this more efficient by allocating a single PVector ad re-using it instead of this basic demo
PVector global = m.mult(local,null);
// copy rectangle and paste it elsewhere
println("local",local,"->global",global);
PImage img = get((int)global.x, (int)global.y, 101, 101);
image(img, 200, 200);
popMatrix();
}
To calculate the position of a vector based on a transformation matrix, simply multiply the vector by that matrix. Very roughly speaking what's what happens with push/pop matrix (a transformation matrix is used for each push/pop stack, which is then multiplied all the way up the global coordinate system). (Notice the comment on efficienty/pre-allocating matrices and vectors as well).
This will be more verbose in terms of code and may need a bit of planning if you're using a lot of nested transformations, however you have finer control of which transformations you choose to use.
A simpler solution may be to switch to the P3D OpenGL renderer which allows you use screenX(), screenY() to do this conversion. (Also checkout modelX()/modelY())
void settings(){
size(500, 500, P3D);
}
void draw() {
background(255);
pushMatrix();
translate(10, 10);
// Fancy rectangle for visibility
fill(255, 0 ,0);
rect(0, 0, 100, 100);
fill(0, 255, 0);
rect(20, 20, 60, 60);
// local to global coordinate conversion using modelX,modelY
float x = screenX(0, 0, 0);
float y = screenY(0, 0, 0);
println(x,y);
PImage img = get((int)x, (int)y, 101, 101);
image(img, 200, 200);
popMatrix();
}
Bare in mind that you want to grab a rectangle which simply has translation applied. Since get() won't take rotation/scale into account, for more complex cases you may want to convert local to global coordinates of not just the top left point, but also the bottom right one with an offset. The idea is to compute the larger bounding box (with no rotation) around the transformed box so when you call get() the whole area of interest is returned (not just a clipped section).
The software is for real-time analysis of an accelerometer chip. However it won't produce a graph using the array components. When I replace the dat[0/1/2] part in the void drawAxes with an actual number the graph is produced.
Is there any way to get the dat[] array to work in this system, or any alternatives I can use?
dat[]*100 is just to scale up the inserted value.
//Accelerometer graphing programme
//Author: Owain L. Evans, owainlevans#outlook.com
//Takes data from Arduino and produces a 3-Dimensional visualisation
import processing.serial.*;
Serial myPort; //Serial Port
String DataLine; //Input from serial port
float[] dat;
PVector a = new PVector(100, 50, 20);
void setup()
{
size(1024, 720, P3D);
strokeWeight(3);
dat = new float[3];
String portName = Serial.list()[0];
myPort = new Serial(this, portName, 9600);
}
void draw() {
if (myPort.available() > 0)
{DataLine = myPort.readStringUntil('\n');
}
if (DataLine != null) {
println(DataLine);
float dat[] = float(split(DataLine, ",")); //parse comma-separated number string into numbers
println(dat);
println();
}
background(250);
drawAxes(dat[0]*100, dat[1]*100, dat[2]*100); //draw original coordinate system
translate(width * 0.5, height * 0.5, 0);
rotateX(map(mouseY,0,height,-PI,PI));
rotateY(map(mouseX,0,width,-PI,PI));
drawAxes(dat[0]*100, dat[1]*100, dat[2]*100);
pushMatrix();
translate(a.x, a.y, a.z); //isolate coordinate system and draw translated A point
popMatrix();
}
void drawAxes(float x, float y, float z) {
//X - red
stroke(192, 0, 0);
line( 0, 0, 0, x, 0, 0);
//Y - green
stroke(0, 192, 0);
line(0, 0, 0, 0, y, 0);
//Z - blue
stroke(0, 0, 192);
line(0, 0, 0, 0, 0, z);
}
Hard to tell what's going on with the array, but you could add parameters to drawAxes() which would remove the tight coupling to dat and make it re-usable for other sketches:
void drawAxes(float x, float y, float z) {
//X - red
stroke(192, 0, 0);
line( 0, 0, 0, x, 0, 0);
//Y - green
stroke(0, 192, 0);
line(0, 0, 0, 0, y, 0);
//Z - blue
stroke(0, 0, 192);
line(0, 0, 0, 0, 0, z);
}
(e.g. drawAxes(dat[0] * 100, dat[1] * 100, dat[2] * 100);)
Update I understand what you mean: you would like to build a graph of the incoming data and plot.
The issue is at the moment you are only storing a single sample which continuously gets updated.
To plot a graph with multiple values you would need to:
create an array to store multiple samples
update the array (shifting old values as a new one arrives)
iterate through the array and plot each value
Step 1: make an [n-samples][3] 2D array to store incoming data.
For the sake of argument let's store a history of 30 samples:
int sampleDimensions = 3;
// how many samples to record
int numSamples = 30;
// where to store the data
float[][] samples = new float[numSamples][sampleDimensions];
Step 2: Update the samples (shifting old ones, adding new data):
// read a new sample x,y,z entry and append it
void updateSamples(float x, float y, float z) {
// shift oldest samples by 1: count backwards from oldest sample to 2nd sample
for (int i = numSamples - 1; i > 0; i--) {
// copy previous sample data: offsetting old data by 1
samples[i][0] = samples[i-1][0];
samples[i][1] = samples[i-1][1];
samples[i][2] = samples[i-1][2];
}
// add newest entry as the first sample (replacing old data)
samples[0][0] = x;
samples[0][1] = y;
samples[0][2] = z;
}
Step 3 Iterate through the data and plot:
// render samples on screen with the option to scale the data
void drawSamples(float scale) {
for (int i = 0 ; i < numSamples; i++){
drawAxes(samples[i][0] * scale, samples[i][1] * scale, samples[i][2] * scale);
}
}
Bare in mind this isn't super efficient in terms of rendering, however for 30 samples should be fine. In the end it's the legibility of the visualisation style that matters.
If you want to do a simpler to render visualisation you could simply render a line:
void drawSamples(float scale) {
beginShape();
for (int i = 0 ; i < numSamples; i++){
vertex(samples[i][0] * scale, samples[i][1] * scale, samples[i][2] * scale);
}
endShape();
}
Putting the whole thing together:
import processing.serial.*;
Serial myPort; //Serial Port
String dataLine; //Input from serial port
PVector a = new PVector(100, 50, 20);
int sampleDimensions = 3;
// how many samples to record
int numSamples = 30;
// where to store the data
float[][] samples = new float[numSamples][sampleDimensions];
// stores a single data sample from serial
float[] sample = new float[sampleDimensions];
// how much to scale data for visualisation
float sampleRenderScale = 100;
void setup() {
size(1024, 720, P3D);
strokeWeight(3);
noFill();
// try to open serial connection: display error otherwise (port missing, or busy (already open in Serial Monitor, etc.))
try {
String portName = Serial.list()[0];
myPort = new Serial(this, portName, 9600);
}catch(Exception e) {
println("error openin serial port: " + Serial.list()[0]);
e.printStackTrace();
}
}
void draw() {
if(myPort != null){
if (myPort.available() > 0){
dataLine = myPort.readStringUntil('\n');
if (dataLine != null) {
println(dataLine);
sample = float(split(dataLine, ",")); //parse comma-separated number string into numbers
println(sample);
if(sample == null || sample.length < 3){
println("parsed less than the expected 3 values:");
return;
}
println();
// add new sample to the list of recorded samples
updateSamples(sample[0], sample[1], sample[2]);
}
}
}
background(250);
drawAxes(sample[0] * sampleRenderScale, sample[1] * sampleRenderScale, sample[2] * sampleRenderScale); //draw original coordinate system
translate(width * 0.5, height * 0.5, 0);
rotateX(map(mouseY, 0, height, -PI, PI));
rotateY(map(mouseX, 0, width, -PI, PI));
drawAxes(sample[0] * sampleRenderScale, sample[1] * sampleRenderScale, sample[2] * sampleRenderScale);
// draw recorded samples
drawSamples(sampleRenderScale);
pushMatrix();
translate(a.x, a.y, a.z); //isolate coordinate system and draw translated A point
popMatrix();
}
// read a new sample x,y,z entry and append it
void updateSamples(float x, float y, float z) {
// shift oldest samples by 1: count backwards from oldest sample to 2nd sample
for (int i = numSamples - 1; i > 0; i--) {
// copy previous sample data: offsetting old data by 1
samples[i][0] = samples[i-1][0];
samples[i][1] = samples[i-1][1];
samples[i][2] = samples[i-1][2];
}
// add newest entry as the first sample (replacing old data)
samples[0][0] = x;
samples[0][1] = y;
samples[0][2] = z;
}
// render samples on screen with the option to scale the data
void drawSamples(float scale) {
beginShape();
for (int i = 0 ; i < numSamples; i++){
vertex(samples[i][0] * scale, samples[i][1] * scale, samples[i][2] * scale);
}
endShape();
}
void drawAxes(float x, float y, float z) {
//X - red
stroke(192, 0, 0);
line( 0, 0, 0, x, 0, 0);
//Y - green
stroke(0, 192, 0);
line(0, 0, 0, 0, y, 0);
//Z - blue
stroke(0, 0, 192);
line(0, 0, 0, 0, 0, z);
}
Notice a few changes:
DataLine renamed to dataLine (as per Java Naming Conventions)
generally renamed variables to it's easy to understand what they are/do and formatting the code so it's easier to read
error handling serial port connection. You already did error handling for incoming data which is great.
The idea is to build good habbits as you progress.
If you have time to explore other languages I recommend Python and few modules,
potentially to start cluserting/classifying the sensor data if that's of interest:
pyserial for serial communication
SciPy: numpy for fast array manipulation, matplotlib for plotting and sklearn for classiciation/clustering/etc.
I am using saveFrame to create an image sequence to bring into after effects. At each loop, I'm upping the frameRate - which I'm sure is not the best way to go about thing. At the end of each loop, I'm saving the frame, but saveFrame can't keep up with the progressively higher frameRate I'm trying to save at. Anyone have an idea how to achieve the effect I'm going for without upping the frameRate, so that saveFrame can keep up? Here's my code:
```
int w = 640; // canvas size
int h = 480;
int n = 10; // number of grid cells
int d = w/n; // diameter of a grid cell
float depth = 0.5; // relative cell depth
int fr = 100;
int iterator = 0;
boolean doSaveFrames = false;
void setup() {
size(w, h, P3D);
rectMode(CENTER);
background(0);
fill(51, 255, 0);
noStroke();
frameRate(fr);
}
void draw() {
// get coordinates
int xy = frameCount % (n*n);
// shift image in z-direction
if (xy == 0) {
PImage img = get();
background(0);
pushMatrix();
translate(0, 0, -d * depth);
tint(255, 127);
image(img, 0, 0);
popMatrix();
// fr+=iterator*10;
// frameRate(fr); //MH - really cool but I can't export fast enough
iterator++;
}
// scale and rotate the square
scale(d);
translate(xy%n + .5, xy/n + .5, -depth * .5 );
rotate(QUARTER_PI - HALF_PI *int(random(2)));
rotateX(HALF_PI);
// draw the square
rect(0, 0, sqrt(2), depth);
if (doSaveFrames) {
saveFrame("frames/line-######.tga");
}
}
```
Instead of basing your animation off of the frameCount variable, create your own variable that you increment at your own speed, and then increase that speed over time. Keep your framerate the same, but increase your animation speed.
To draw a dotted line in OpenGL I can use glLineStipple, but how do I achieve the same effect in OpenGL ES 1?
Lines can be textured, just like triangles. Enable alpha testing, apply an alpha texture, set up some texture coordinates, and enjoy.
Actually i have realized the doted line or the dashed line using for loops but it still make non sense to use it as a line type link to the drawing method, here is the code of my doted line and dashed line below:
doted line:
(void)drawVerticalDotedInternalGrid{
float a,b;
int drawCount =0;
GLfloat dotedInternalGrid[1296];
for (a = -0.5f; a <= 0.5f; a +=0.5f) {
for (b = -0.875f; b <=0.925f; b += 0.025f)
{
dotedInternalGrid[drawCount] = b;
drawCount++;
dotedInternalGrid[drawCount] = a;
drawCount++;
};
};
glPointSize(1.0f);
glColor4f(0.863f,0.863f,0.863f,0.8f); //line color
glVertexPointer(2, GL_FLOAT, 0, dotedInternalGrid);
glEnableClientState(GL_VERTEX_ARRAY);
glDrawArrays(GL_POINTS, 0, 648);
glDisableClientState(GL_VERTEX_ARRAY);
}
dashed line:
(void)drawVerticalDashedInternalGridH{
GLfloat dashedLine[1296];
float a,b;
int i =0;
//-0.4----0.4 // -0.875----0.900
for (a = -0.4f; a <= 0.4f; a +=0.1f) {
for (b =-0.825f; b <=0.950f; b+=0.025f) {
dashedLine[i] = b;
i++;
dashedLine[i] = a;
i++;
};
};
//glLineWidth(1.0f);
glColor4f(0.863f,0.863f,0.863f,1.f); //line color
glVertexPointer(2, GL_FLOAT, 0, dashedLine);
glEnableClientState(GL_VERTEX_ARRAY);
glDrawArrays(GL_LINES, 0, 648);
glDisableClientState(GL_VERTEX_ARRAY);
}
of course ye can see the code is drawing in a rectangle area of certain coordinates,the bother things is how to figure out the dotedInternalGrid[1296]; this size of array dynamically for draw method use and the number of lines to draw as well.
To explain it easily, I have put drawHorizontalDashedLine() first.
To understand, click this image.
I cannot put an image on this post because of my reputation.
Visualizing the Vertices
+(void)drawHorizontalDashedLine:(GLfloat)x1 x2:(GLfloat)x2 y:(GLfloat)y {
//Parameters
GLfloat DASH_LENGTH = 4.0f;
GLfloat GAP_LENGTH = 2.0f;
GLfloat LINE_THICKNESS = 1.5f;
//Calculate how many dashes require to draw the whole line
GLfloat fHorizontalLength = fabsf(x2-x1);
int nDashedLineCount = fHorizontalLength / (DASH_LENGTH + GAP_LENGTH);
int nVerticesSize = nDashedLineCount * 4; //A dashed line has 4 values(2 points)
//Vertex
GLfloat vertices[nVerticesSize];
//The first dashed line vertices
vertices[0] = (x1 < x2)? x1 : x2;
vertices[1] = y;
vertices[2] = (x1 < x2)? x1 : x2 + DASH_LENGTH;
vertices[3] = y;
//The other vertices of dashed lines
for (int nIndex=4; nIndex < nVerticesSize; nIndex=nIndex+4) {
vertices[nIndex] = vertices[nIndex-2] + GAP_LENGTH;
vertices[nIndex+1] = y;
vertices[nIndex+2] = vertices[nIndex] + DASH_LENGTH;
vertices[nIndex+3] = y;
//NSLog(#"Point1(%.2f, %.2f)", vertices[nIndex], vertices[nIndex+1]);
//NSLog(#"Point2(%.2f, %.2f)", vertices[nIndex+2], vertices[nIndex+3]);
}
//Draw the arrays
glPushMatrix();
glLineWidth(LINE_THICKNESS);
glVertexPointer (2, GL_FLOAT, 0, vertices);
glDrawArrays (GL_LINES, 0, nVerticesSize/2);
glPopMatrix();
}
drawDashedLine().
I used the trigonometric function to get lengths.
+(void)drawDashedLine:(CGPoint)point1 point2:(CGPoint)point2 {
//Parameters
GLfloat DASH_LENGTH = 3.0f;
GLfloat GAP_LENGTH = 1.0f;
GLfloat LINE_THICKNESS = 1.5f;
//Calculate how many dashes require to draw the whole line
GLfloat fWidth = point2.x - point1.x;
GLfloat fHeight = point2.y - point1.y;
GLfloat fRadian = atan2(fHeight, fWidth);
float fLineLength = sqrtf(powf(fWidth, 2) + powf(fHeight, 2));
int nDashedLineCount = fabsf(fLineLength / (DASH_LENGTH + GAP_LENGTH));
int nVerticesSize = nDashedLineCount * 4; //A dashed line has 4 values(2 points)
//Vertex
GLfloat vertices[nVerticesSize];
//The first dashed line vertices
vertices[0] = point1.x;
vertices[1] = point1.y;
vertices[2] = point1.x + cosf(fRadian) * DASH_LENGTH;
vertices[3] = point1.y + sinf(fRadian) * DASH_LENGTH;
//The other vertices of dashed lines
for (int nIndex=4; nIndex < nVerticesSize; nIndex=nIndex+4) {
vertices[nIndex] = vertices[nIndex-2] + cosf(fRadian) * GAP_LENGTH;
vertices[nIndex+1] = vertices[nIndex-1] + sinf(fRadian) * GAP_LENGTH;
vertices[nIndex+2] = vertices[nIndex] + cosf(fRadian) * DASH_LENGTH;
vertices[nIndex+3] = vertices[nIndex+1] + sinf(fRadian) * DASH_LENGTH;
//NSLog(#"DrawDash Point1(%.2f, %.2f)", vertices[nIndex], vertices[nIndex+1]);
//NSLog(#"DrawDash Point2(%.2f, %.2f)", vertices[nIndex+2], vertices[nIndex+3]);
}
//Draw the arrays
glPushMatrix();
glLineWidth(LINE_THICKNESS);
glVertexPointer (2, GL_FLOAT, 0, vertices);
glDrawArrays (GL_LINES, 0, nVerticesSize/2);
glPopMatrix();
}
glPushAttrib(GL_ENABLE_BIT);
# glPushAttrib is done to return everything to normal after drawing
glLineStipple(1, 0xAAAA); # [1]
glEnable(GL_LINE_STIPPLE);
glBegin(GL_LINES);
glVertex3f(-.5,.5,-.5);
glVertex3f(.5,.5,-.5);
glEnd();
glPopAttrib();