/* Copyright 2008 Tomasz Kaczmarzyk Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ import processing.opengl.*; import controlP5.*; ControlP5 controlP5; Slider s; Slider sg; int COUNT = 20; // gęstość siatki float[][] cpX, cpY, cpZ; // punkty kontrolne float ax, ay, az; boolean light = true; boolean grid = false; boolean normals = true; boolean points = true; int typeX = 0; int typeY = 0; void setup() { size(600,600,OPENGL); controlP5 = new ControlP5(this); s = controlP5.addSlider("rotation",0,360,126,3,582,332,17); s.setSliderMode(0); sg = controlP5.addSlider("grid_size",2,30,20,3,562,332,17); sg.setSliderMode(0); controlP5.addButton("control points",10,3,2,80,18).setId(1); controlP5.addButton("grid ",10,86,2,55,18).setId(2); controlP5.addButton("normals ",10,144,2,90,18).setId(3); controlP5.addButton("kind of bicubic path",10,237,2,100,18).setId(4); ax = ay = az = 0; ax = 3 * PI/10; az = 7 * PI/10; cpX = new float[][] { { 0.23 * width, 0.41 * width, 0.59 * width, 0.77 * width }, { 0.23 * width, 0.41 * width, 0.59 * width, 0.77 * width }, { 0.23 * width, 0.41 * width, 0.59 * width, 0.77 * width }, { 0.23 * width, 0.41 * width, 0.59 * width, 0.77 * width } }; cpY = new float[][] { { 0.23 * height, 0.23 * height, 0.23 * height, 0.23 * height }, { 0.41 * height, 0.41 * height, 0.41 * height, 0.41 * height }, { 0.59 * height, 0.59 * height, 0.59 * height, 0.59 * height }, { 0.77 * height, 0.77 * height, 0.77 * height, 0.77 * height } }; cpZ = new float[][] { { 0, 0, 0, 0}, { 0, 0.3 * width, 0.3 * width, 0}, { 0, 0.3 * width, 0.3 * width, 0}, { 0, 0, 0, 0} }; } float[] calculateNormals(float[] v1, float[] v2) { if (v1.length == 3 && v2.length == 3) { float[] n = new float[3]; n[0] = v1[1] * v2[2] - v1[2] * v2[1]; n[1] = v1[2] * v2[0] - v1[0] * v2[2]; n[2] = v1[0] * v2[1] - v1[1] * v2[0]; // normalizacja float l = sqrt(pow(n[0],2) + pow(n[1],2) + pow(n[2],2)); if (l > 0) { n[0] /= l; n[1] /= l; n[2] /= l; } return n; } return null; } void bicubic_patch(int tu, int tv,float[][] cpX, float[][] cpY, float[][] cpZ) { float[][] duX, duY, duZ; float[][] dvX, dvY, dvZ; float[][][] NP; // wektory normalne float[][] mp0 = { {-1, 3,-3, 1 }, { 3,-6, 3, 0 }, {-3, 3, 0, 0 }, { 1, 0, 0, 0 } }; float[][] mp1 = { {-0.5, 1.0,-0.5, 0.0 }, { 1.5,-2.5, 0.0, 1.0 }, {-1.5, 2.0, 0.5, 0.0 }, { 0.5,-0.5, 0.0, 0.0 } }; float[][] mp2 = { {-1.0/6, 3.0/6,-3.0/6, 1.0/6 }, { 3.0/6,-6.0/6, 0, 4.0/6 }, {-3.0/6, 3.0/6, 3.0/6, 1.0/6 }, { 1.0/6, 0, 0, 0 } }; float[][] mp3 = { { 2,-3, 0, 1 }, { 1,-2, 1, 0 }, {-2, 3, 0, 0 }, { 1,-1, 0, 0 } }; float[][] mp4 = { {-1, 1, -0.5, 2 }, { 2.5, -2.5, 0, 0 }, {-3, 2, 0.5, 0 }, { 1, -0.5, 0, 0 } }; float[][] mpu, mpv; if (tu == 1) mpu = mp1; else if (tu == 2) mpu = mp2; else if (tu == 3) mpu = mp3; else if (tu == 4) mpu = mp4; else mpu = mp0; if (tv == 1) mpv = mp1; else if (tv == 2) mpv = mp2; else if (tv == 3) mpv = mp3; else if (tv == 4) mpv = mp4; else mpv = mp0; float[][] fpX = new float[COUNT+1][COUNT+1]; float[][] fpY = new float[COUNT+1][COUNT+1]; float[][] fpZ = new float[COUNT+1][COUNT+1]; Matrix PX = new Matrix(cpX); Matrix PY = new Matrix(cpY); Matrix PZ = new Matrix(cpZ); Matrix MPU = new Matrix(mpu); Matrix MPUT = MPU.transpose(); Matrix MPV = new Matrix(mpv); Matrix MPVT = MPV.transpose(); for (int i = 0; i <= COUNT; i++) { float u = float(i) / float(COUNT); float[][] mu = { {pow(u,3), pow(u,2), u, 1} }; Matrix MU = new Matrix(mu); MU = MU.times(MPUT); Matrix MUX = MU.times(PX); Matrix MUY = MU.times(PY); Matrix MUZ = MU.times(PZ); for (int j = 0; j <= COUNT; j++) { float v = float(j) / float(COUNT); float[][] mv = { {pow(v,3)}, {pow(v,2)}, {v}, {1} }; Matrix MV = new Matrix(mv); MV = MPV.times(MV); Matrix X = MUX.times(MV); Matrix Y = MUY.times(MV); Matrix Z = MUZ.times(MV); fpX[i][j] = X.getVal(0,0); fpY[i][j] = Y.getVal(0,0); fpZ[i][j] = Z.getVal(0,0); } } // obliczenie wektorów normalnych duX = new float[COUNT+1][COUNT+1]; duY = new float[COUNT+1][COUNT+1]; duZ = new float[COUNT+1][COUNT+1]; for (int i = 0; i <= COUNT; i++) { float u = float(i) / float(COUNT); float[][] mu = { { 3 * pow(u,2), 2 * u, 1, 0} }; Matrix MU = new Matrix(mu); MU = MU.times(MPUT); Matrix MUX = MU.times(PX); Matrix MUY = MU.times(PY); Matrix MUZ = MU.times(PZ); for (int j = 0; j <= COUNT; j++) { float v = float(j) / float(COUNT); float[][] mv = { {pow(v,3)}, {pow(v,2)}, {v}, {1} }; Matrix MV = new Matrix(mv); MV = MPV.times(MV); Matrix X = MUX.times(MV); Matrix Y = MUY.times(MV); Matrix Z = MUZ.times(MV); duX[i][j] = X.getVal(0,0); duY[i][j] = Y.getVal(0,0); duZ[i][j] = Z.getVal(0,0); } } dvX = new float[COUNT+1][COUNT+1]; dvY = new float[COUNT+1][COUNT+1]; dvZ = new float[COUNT+1][COUNT+1]; for (int i = 0; i <= COUNT; i++) { float u = float(i) / float(COUNT); float[][] mu = { {pow(u,3), pow(u,2), u, 1} }; Matrix MU = new Matrix(mu); MU = MU.times(MPUT); Matrix MUX = MU.times(PX); Matrix MUY = MU.times(PY); Matrix MUZ = MU.times(PZ); for (int j = 0; j <= COUNT; j++) { float v = float(j) / float(COUNT); float[][] mv = { {3 * pow(v,2)}, { 2 * v}, {1}, {0} }; Matrix MV = new Matrix(mv); MV = MPV.times(MV); Matrix X = MUX.times(MV); Matrix Y = MUY.times(MV); Matrix Z = MUZ.times(MV); dvX[i][j] = X.getVal(0,0); dvY[i][j] = Y.getVal(0,0); dvZ[i][j] = Z.getVal(0,0); } } NP = new float[COUNT+1][COUNT+1][3]; for (int i = 0; i < COUNT+1; i++) for (int j = 0; j < COUNT+1; j++) { float[] p1 = {duX[i][j],duY[i][j],duZ[i][j]}; float[] p2 = {dvX[i][j],dvY[i][j],dvZ[i][j]}; float[] nP = calculateNormals(p1,p2); for (int k = 0; k < 3; k++) { NP[i][j][k] = nP[k]; } } for (int i = 0; i < COUNT; i++) for (int j = 0; j < COUNT; j++) { beginShape(); if (normals) { normal(NP[i][j][0],NP[i][j][1],NP[i][j][2]); } vertex(fpX[i][j],fpY[i][j],fpZ[i][j]); if (normals) { normal(NP[i+1][j][0],NP[i+1][j][1],NP[i+1][j][2]); } vertex(fpX[i+1][j],fpY[i+1][j],fpZ[i+1][j]); if (normals) { normal(NP[i][j+1][0],NP[i][j+1][1],NP[i][j+1][2]); } vertex(fpX[i][j+1],fpY[i][j+1],fpZ[i][j+1]); endShape(CLOSE); beginShape(); if (normals) { normal(NP[i+1][j][0],NP[i+1][j][1],NP[i+1][j][2]); } vertex(fpX[i+1][j],fpY[i+1][j],fpZ[i+1][j]); if (normals) { normal(NP[i+1][j+1][0],NP[i+1][j+1][1],NP[i+1][j+1][2]); } vertex(fpX[i+1][j+1],fpY[i+1][j+1],fpZ[i+1][j+1]); if (normals) { normal(NP[i][j+1][0],NP[i][j+1][1],NP[i][j+1][2]); } vertex(fpX[i][j+1],fpY[i][j+1],fpZ[i][j+1]); endShape(CLOSE); } } void draw() { background(0); if(light) lights(); pushMatrix(); translate(width/2,height/2,0); rotateX(ax); rotateY(ay); rotateZ(az); translate(-width/2,-height/2,0); noStroke(); fill(#00ff00); if (points) for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) { translate(cpX[i][j],cpY[i][j],cpZ[i][j]); sphere(3); translate(-cpX[i][j],-cpY[i][j],-cpZ[i][j]); } if (grid) { noFill(); stroke(#ff0000); } else { fill(#ffff00); noStroke(); } bicubic_patch(typeX, typeY, cpX, cpY, cpZ); popMatrix(); controlP5.draw(); } void controlEvent(ControlEvent theEvent) { switch(theEvent.controller().id()) { case(1): points = !points; break; case(2): grid = !grid; break; case(3): normals = !normals; break; case(4): nextType(); break; } } int type = 0; void nextType() { type++; if (type == 4) type = 0; switch (type) { case 0: typeX=0; typeY=0; break; case 1: typeX=1; typeY=1; break; case 2: typeX=2; typeY=2; break; case 3: typeX=0; typeY=1; break; } } void rotation(float rot) { az = rot / 180 * PI; } void grid_size(float rot) { COUNT = (int)rot; }