void ArchesModel::InitModel() { int NumArches=parm1; int SegmentsPerArch=parm2; arc = wxAtoi(ModelXml->GetAttribute("arc", "180")); SetBufferSize(NumArches,SegmentsPerArch); if (SingleNode) { SetNodeCount(NumArches * SegmentsPerArch, parm3,rgbOrder); } else { SetNodeCount(NumArches, SegmentsPerArch, rgbOrder); if (parm3 > 1) { for (int x = 0; x < Nodes.size(); x++) { Nodes[x]->Coords.resize(parm3); } } } screenLocation.SetRenderSize(SegmentsPerArch, NumArches); for (int y=0; y < NumArches; y++) { for(int x=0; x<SegmentsPerArch; x++) { int idx = y * SegmentsPerArch + x; Nodes[idx]->ActChan = stringStartChan[y] + x*GetNodeChannelCount(StringType); Nodes[idx]->StringNum=y; for(size_t c=0; c < GetCoordCount(idx); c++) { Nodes[idx]->Coords[c].bufX=IsLtoR ? x : SegmentsPerArch-x-1; Nodes[idx]->Coords[c].bufY=isBotToTop ? y : NumArches-y-1; } } } SetArchCoord(); }
// initialize buffer coordinates // parm1=Number of Strings/Arches // parm2=Pixels Per String/Arch void ModelClass::InitLine() { SetNodeCount(parm1,parm2); SetBufferSize(1,parm2); int LastStringNum=-1; int chan,idx; int ChanIncr=SingleChannel ? 1 : 3; size_t NodeCount=GetNodeCount(); for(size_t n=0; n<NodeCount; n++) { if (Nodes[n]->StringNum != LastStringNum) { LastStringNum=Nodes[n]->StringNum; chan=stringStartChan[LastStringNum]; idx=0; } Nodes[n]->ActChan=chan; chan+=ChanIncr; size_t CoordCount=GetCoordCount(n); for(size_t c=0; c < CoordCount; c++) { Nodes[n]->Coords[c].bufX=IsLtoR ? idx : parm2-idx-1; Nodes[n]->Coords[c].bufY=0; idx++; } } }
// initialize screen coordinates for tree void ModelClass::SetTreeCoord(long degrees) { int bufferX, bufferY; double angle,x0; TreeDegrees=degrees; if (BufferWi < 2) return; if(BufferHt<1) return; // June 27,2013. added check to not divide by zero int factor=1000/BufferHt; RenderHt=BufferHt*factor; RenderWi=RenderHt/2; double radians=toRadians(degrees); double radius=RenderWi/2.0; double StartAngle=-radians/2.0; double AngleIncr=radians/double(BufferWi-1); //wxString msg=wxString::Format("BufferHt=%d, BufferWi=%d, factor=%d, RenderHt=%d, RenderWi=%d\n",BufferHt,BufferWi,factor,RenderHt,RenderWi); size_t NodeCount=GetNodeCount(); for(size_t n=0; n<NodeCount; n++) { size_t CoordCount=GetCoordCount(n); for(size_t c=0; c < CoordCount; c++) { bufferX=Nodes[n]->Coords[c].bufX; bufferY=Nodes[n]->Coords[c].bufY; angle=StartAngle + double(bufferX) * AngleIncr; x0=radius * sin(angle); Nodes[n]->Coords[c].screenX=floor(x0*(1.0-double(bufferY)/double(BufferHt)) + 0.5); Nodes[n]->Coords[c].screenY=bufferY * factor; } } }
// parm3 is number of points // top left=top ccw, top right=top cw, bottom left=bottom cw, bottom right=bottom ccw void ModelClass::InitStar() { if (parm3 < 2) parm3=2; // need at least 2 arms SetNodeCount(parm1,parm2); int numlights=parm1*parm2; SetBufferSize(numlights+1,numlights+1); int LastStringNum=-1; int chan,cursegment,nextsegment,x,y; int offset=numlights/2; int numsegments=parm3*2; double segstart_x,segstart_y,segend_x,segend_y,segstart_pct,segend_pct,r,segpct,dseg; double dpct=1.0/(double)numsegments; double OuterRadius=offset; double InnerRadius=OuterRadius/2.618034; // divide by golden ratio squared double pct=isBotToTop ? 0.5 : 0.0; // % of circle, 0=top double pctIncr=1.0 / (double)numlights; // this is cw if (IsLtoR != isBotToTop) pctIncr*=-1.0; // adjust to ccw int ChanIncr=SingleChannel ? 1 : 3; size_t NodeCount=GetNodeCount(); for(size_t n=0; n<NodeCount; n++) { if (Nodes[n]->StringNum != LastStringNum) { LastStringNum=Nodes[n]->StringNum; chan=stringStartChan[LastStringNum]; } Nodes[n]->ActChan=chan; chan+=ChanIncr; size_t CoordCount=GetCoordCount(n); for(size_t c=0; c < CoordCount; c++) { cursegment=(int)((double)numsegments*pct) % numsegments; nextsegment=(cursegment+1) % numsegments; segstart_pct=(double)cursegment / numsegments; segend_pct=(double)nextsegment / numsegments; dseg=pct - segstart_pct; segpct=dseg / dpct; r=cursegment%2==0 ? OuterRadius : InnerRadius; segstart_x=r*sin(segstart_pct*2.0*M_PI); segstart_y=r*cos(segstart_pct*2.0*M_PI); r=nextsegment%2==0 ? OuterRadius : InnerRadius; segend_x=r*sin(segend_pct*2.0*M_PI); segend_y=r*cos(segend_pct*2.0*M_PI); // now interpolate between segstart and segend x=(segend_x - segstart_x)*segpct + segstart_x + offset + 0.5; y=(segend_y - segstart_y)*segpct + segstart_y + offset + 0.5; Nodes[n]->Coords[c].bufX=x; Nodes[n]->Coords[c].bufY=y; pct+=pctIncr; if (pct >= 1.0) pct-=1.0; if (pct < 0.0) pct+=1.0; } } }
int ModelClass::FindChannelAt(int x, int y) { size_t NodeCount=GetNodeCount(); for(size_t n=0; n<NodeCount; n++) { size_t CoordCount=GetCoordCount(n); for(size_t c=0; c < CoordCount; c++) { //?? if ((Nodes[n]->Coords[c].screenX == x) && (Nodes[n]->Coords[c].screenY == y)) return Nodes[n].ActChan; if ((Nodes[n]->Coords[c].bufX == x) && (Nodes[n]->Coords[c].bufY == y)) return Nodes[n]->ActChan; } } return -1; //not found }
// uses DrawCircle instead of DrawPoint void ModelClass::DisplayEffectOnWindow(wxWindow* window) { wxColour color; wxDouble w, h; ModelGraphics gc(window); gc.GetSize(&w, &h); double scaleX = double(w) * 0.95 / RenderWi; double scaleY = double(h) * 0.95 / RenderHt; double scale=scaleY < scaleX ? scaleY : scaleX; gc.Translate(w/2,-int(double(RenderHt)*scale + double(RenderHt)*0.025*scale)); double radius = scale/2.0; if (radius < 0.5) { radius = 0.5; } /* // check that origin is in the right place color.Set(0,0,255); gc.AddCircle(color, 0,0,1); gc.AddCircle(color, 1,1,1); gc.AddCircle(color, 2,2,1); gc.AddCircle(color, 3,3,1); gc.AddCircle(color, 4,4,1); */ // layer calculation and map to output size_t NodeCount=Nodes.size(); double sx,sy; for(size_t n=0; n<NodeCount; n++) { Nodes[n]->GetColor(color); size_t CoordCount=GetCoordCount(n); for(size_t c=0; c < CoordCount; c++) { // draw node on screen sx=Nodes[n]->Coords[c].screenX; sy=Nodes[n]->Coords[c].screenY; //# dc.DrawPoint(Nodes[i].screenX, Nodes[i].screenY); //dc.DrawPoint(sx,sy); //dc.DrawCircle(sx*scale,sy*scale,radius); //gc->DrawEllipse(sx*scale,sy*scale,radius,radius); gc.AddCircle(color, sx*scale,sy*scale,radius); } } }
// initialize screen coordinates void ModelClass::CopyBufCoord2ScreenCoord() { size_t NodeCount=GetNodeCount(); int xoffset=BufferWi/2; for(size_t n=0; n<NodeCount; n++) { size_t CoordCount=GetCoordCount(n); for(size_t c=0; c < CoordCount; c++) { Nodes[n]->Coords[c].screenX = Nodes[n]->Coords[c].bufX - xoffset; Nodes[n]->Coords[c].screenY = Nodes[n]->Coords[c].bufY; } } SetRenderSize(BufferHt,BufferWi); }
// Set screen coordinates for arches void ModelClass::SetArchCoord() { int xoffset,x,y; int numlights=parm1*parm2; size_t NodeCount=GetNodeCount(); SetRenderSize(parm2,numlights*2); double midpt=parm2; for(size_t n=0; n<NodeCount; n++) { xoffset=Nodes[n]->StringNum*parm2*2 - numlights; size_t CoordCount=GetCoordCount(n); for(size_t c=0; c < CoordCount; c++) { double angle=-M_PI/2.0 + M_PI * (double)Nodes[n]->Coords[c].bufX/midpt; x=xoffset + (int)floor(midpt*sin(angle)+midpt); y=(int)floor(midpt*cos(angle)+0.5); Nodes[n]->Coords[c].screenX=x; Nodes[n]->Coords[c].screenY=y; } } }
// initialize screen coordinates // parm1=Number of Strings/Arches // parm2=Pixels Per String/Arch void ModelClass::SetLineCoord() { int x,y; int idx=0; size_t NodeCount=GetNodeCount(); int numlights=parm1*parm2; int half=numlights/2; SetRenderSize(numlights*2,numlights); double radians=toRadians(PreviewRotation); for(size_t n=0; n<NodeCount; n++) { size_t CoordCount=GetCoordCount(n); for(size_t c=0; c < CoordCount; c++) { x=cos(radians)*idx; x=IsLtoR ? x - half : half - x; y=sin(radians)*idx; Nodes[n]->Coords[c].screenX=x; Nodes[n]->Coords[c].screenY=y + numlights; idx++; } } }
void ArchesModel::SetArchCoord() { double xoffset,x,y; size_t NodeCount=GetNodeCount(); double midpt=parm2*parm3; midpt -= 1.0; midpt /= 2.0; double total = toRadians(arc); double start = (M_PI - total) / 2.0; double angle=-M_PI/2.0 + start; x=midpt*sin(angle)*2.0+parm2*parm3; double width = parm2*parm3*2 - x; double minY = 999999; for(size_t n=0; n<NodeCount; n++) { xoffset=Nodes[n]->StringNum*width; size_t CoordCount=GetCoordCount(n); for(size_t c=0; c < CoordCount; c++) { double angle=-M_PI/2.0 + start + total * ((double)(Nodes[n]->Coords[c].bufX * parm3 + c))/midpt/2.0; x=xoffset + midpt*sin(angle)*2.0+parm2*parm3; y=(parm2*parm3)*cos(angle); Nodes[n]->Coords[c].screenX=x; Nodes[n]->Coords[c].screenY=y; minY = std::min(minY, y); } } float renderHt = parm2*parm3; if (minY > 1) { renderHt -= minY; for (auto it = Nodes.begin(); it != Nodes.end(); it++) { for (auto coord = (*it)->Coords.begin(); coord != (*it)->Coords.end(); coord++) { coord->screenY -= minY; } } } screenLocation.SetRenderSize(width*parm1, renderHt); }
// display model using a single color void ModelClass::DisplayModelOnWindow(wxWindow* window, const wxColour* color) { size_t NodeCount=Nodes.size(); wxCoord sx,sy; wxPen pen; wxDouble w, h; ModelGraphics gc(window); /* // this isn't an ideal scaling algorithm - room for improvement here double windowDiagonal=sqrt(w*w+h*h); double modelDiagonal=sqrt(RenderWi*RenderWi+RenderHt*RenderHt); double scale=windowDiagonal / modelDiagonal * PreviewScale; */ gc.GetSize(&w, &h); double scale=RenderHt > RenderWi ? double(h) / RenderHt * PreviewScale : double(w) / RenderWi * PreviewScale; double scrx,scry; gc.Translate(int(offsetXpct*w)+w/2, -(int(offsetYpct*h)+h- std::max((int(h)-int(double(RenderHt-1)*scale))/2,1))); for(size_t n=0; n<NodeCount; n++) { size_t CoordCount=GetCoordCount(n); for(size_t c=0; c < CoordCount; c++) { // draw node on screen sx=Nodes[n]->Coords[c].screenX; sy=Nodes[n]->Coords[c].screenY; gc.AddSquare(*color,sx*scale,sy*scale,0.0); scrx = sx*scale; scry = sy*scale; // StatusBar1->SetStatusText(_("Status: DisplayModelOnWindow " )+wxString::Format(" x=%5ld y=%5ld ",scrx,scry)); } } }
// top left=top ccw, top right=top cw, bottom left=bottom cw, bottom right=bottom ccw void ModelClass::InitWreath() { SetNodeCount(parm1,parm2); int numlights=parm1*parm2; SetBufferSize(numlights+1,numlights+1); int LastStringNum=-1; int offset=numlights/2; double r=offset; int chan,x,y; double pct=isBotToTop ? 0.5 : 0.0; // % of circle, 0=top double pctIncr=1.0 / (double)numlights; // this is cw if (IsLtoR != isBotToTop) pctIncr*=-1.0; // adjust to ccw int ChanIncr=SingleChannel ? 1 : 3; size_t NodeCount=GetNodeCount(); for(size_t n=0; n<NodeCount; n++) { if (Nodes[n]->StringNum != LastStringNum) { LastStringNum=Nodes[n]->StringNum; chan=stringStartChan[LastStringNum]; } Nodes[n]->ActChan=chan; chan+=ChanIncr; size_t CoordCount=GetCoordCount(n); for(size_t c=0; c < CoordCount; c++) { x=r*sin(pct*2.0*M_PI) + offset + 0.5; y=r*cos(pct*2.0*M_PI) + offset + 0.5; Nodes[n]->Coords[c].bufX=x; Nodes[n]->Coords[c].bufY=y; pct+=pctIncr; if (pct >= 1.0) pct-=1.0; if (pct < 0.0) pct+=1.0; } } }
// initialize buffer coordinates // parm1=Nodes on Top // parm2=Nodes left and right // parm3=Nodes on Bottom void ModelClass::InitFrame() { int x,y,newx,newy; SetNodeCount(1,parm1+2*parm2+parm3); int FrameWidth=std::max(parm1,parm3)+2; SetBufferSize(parm2,FrameWidth); // treat as outside of matrix //SetBufferSize(1,Nodes.size()); // treat as single string SetRenderSize(parm2,FrameWidth); int chan=stringStartChan[0]; int ChanIncr=SingleChannel ? 1 : 3; int xincr[4]= {0,1,0,-1}; // indexed by side int yincr[4]= {1,0,-1,0}; x=IsLtoR ? 0 : FrameWidth-1; y=isBotToTop ? 0 : parm2-1; int dir=1; // 1=clockwise int side=x>0 ? 2 : 0; // 0=left, 1=top, 2=right, 3=bottom int SideIncr=1; // 1=clockwise if ((parm1 > parm3 && x>0) || (parm3 > parm1 && x==0)) { // counter-clockwise dir=-1; SideIncr=3; } // determine starting position if (parm1 > parm3) { // more nodes on top, must start at bottom y=0; } else if (parm3 > parm1) { // more nodes on bottom, must start at top y=parm2-1; } else { // equal top and bottom, can start in any corner // assume clockwise numbering if (x>0 && y==0) { // starting in lower right side=3; } else if (x==0 && y>0) { // starting in upper left side=1; } } size_t NodeCount=GetNodeCount(); for(size_t n=0; n<NodeCount; n++) { Nodes[n]->ActChan=chan; chan+=ChanIncr; size_t CoordCount=GetCoordCount(n); for(size_t c=0; c < CoordCount; c++) { Nodes[n]->Coords[c].bufX=x; Nodes[n]->Coords[c].bufY=y; newx=x+xincr[side]*dir; newy=y+yincr[side]*dir; if (newx < 0 || newx >= FrameWidth || newy < 0 || newy >= parm2) { // move to the next side side=(side+SideIncr) % 4; newx=x+xincr[side]*dir; newy=y+yincr[side]*dir; } x=newx; y=newy; } } }
// display model using colors stored in each node // used when preview is running void ModelClass::DisplayModelOnWindow(wxWindow* window) { size_t NodeCount=Nodes.size(); wxCoord sx,sy; wxPen pen; wxColour color; wxDouble w, h; ModelGraphics gc(window); /* // this isn't an ideal scaling algorithm - room for improvement here double windowDiagonal=sqrt(w*w+h*h); double modelDiagonal=sqrt(RenderWi*RenderWi+RenderHt*RenderHt); double scale=windowDiagonal / modelDiagonal * PreviewScale; */ gc.GetSize(&w, &h); double scale=RenderHt > RenderWi ? double(h) / RenderHt * PreviewScale : double(w) / RenderWi * PreviewScale; gc.Translate(int(offsetXpct*w)+w/2, -(int(offsetYpct*h)+h- std::max((int(h)-int(double(RenderHt-1)*scale))/2,1))); // avoid performing StrobeRate test in inner loop for performance reasons if (StrobeRate==0) { // no strobing for(size_t n=0; n<NodeCount; n++) { Nodes[n]->GetColor(color); size_t CoordCount=GetCoordCount(n); for(size_t c=0; c < CoordCount; c++) { // draw node on screen sx=Nodes[n]->Coords[c].screenX; sy=Nodes[n]->Coords[c].screenY; gc.AddSquare(color,sx*scale,sy*scale,0.0); } } } else { // flash individual nodes according to StrobeRate for(size_t n=0; n<NodeCount; n++) { Nodes[n]->GetColor(color); bool CanFlash = color.GetRGB() == 0x00ffffff; size_t CoordCount=GetCoordCount(n); for(size_t c=0; c < CoordCount; c++) { wxColor c2 = *wxBLACK; // draw node on screen if (CanFlash && rand() % StrobeRate == 0) { c2 = color; } sx=Nodes[n]->Coords[c].screenX; sy=Nodes[n]->Coords[c].screenY; gc.AddSquare(c2,sx*scale,sy*scale,0.0); } } } }
// initialize screen coordinates for tree void TreeModel::SetTreeCoord(long degrees) { double bufferX, bufferY; if (BufferWi < 1) return; if (BufferHt < 1) return; // June 27,2013. added check to not divide by zero double RenderHt, RenderWi; if (degrees > 0) { double angle; RenderHt=BufferHt * 3; RenderWi=((double)RenderHt)/1.8; double radians=toRadians(degrees); double radius=RenderWi/2.0; double topradius=radius/botTopRatio; double StartAngle=-radians/2.0; double AngleIncr=radians/double(BufferWi); if (degrees < 350 && BufferWi > 1) { AngleIncr=radians/double(BufferWi - 1); } //shift a tiny bit to make the strands in back not line up exactly with the strands in front StartAngle += toRadians(rotation); std::vector<float> yPos(BufferHt); std::vector<float> xInc(BufferHt); for (int x = 0; x < BufferHt; x ++) { yPos[x] = x; xInc[x] = 0; } if (spiralRotations != 0.0f) { std::vector<float> lengths(10); float rgap = (radius - topradius)/ 10.0; float total = 0; for (int x = 0; x < 10; x++) { lengths[x] = 2.0*M_PI*(radius - rgap*x) - rgap/2.0; lengths[x] *= spiralRotations / 10.0; lengths[x] = sqrt(lengths[x]*lengths[x]+(float)BufferHt/10.0*(float)BufferHt/10.0); total += lengths[x]; } int lights = 0; for (int x = 0; x < 10; x++) { lengths[x] /= total; lights += (int)std::round(lengths[x]*BufferHt); } int curSeg = 0; float lightsInSeg = std::round(lengths[0]*BufferHt); int curLightInSeg = 0; for (int x = 1; x < BufferHt; x++) { if (curLightInSeg >= lightsInSeg) { curSeg++; curLightInSeg = 0; lightsInSeg = std::round(lengths[curSeg]*BufferHt); } float ang = spiralRotations * 2.0 * M_PI / 10.0; ang /= (float)lightsInSeg; yPos[x] = yPos[x-1] + (BufferHt/10.0/lightsInSeg); xInc[x] = xInc[x-1] + ang; curLightInSeg++; } } double topYoffset = std::abs(perspective * topradius * cos(M_PI)); double ytop = RenderHt - topYoffset; double ybot = std::abs(perspective * radius * cos(M_PI)); size_t NodeCount=GetNodeCount(); for(size_t n=0; n<NodeCount; n++) { size_t CoordCount=GetCoordCount(n); for(size_t c=0; c < CoordCount; c++) { bufferX=Nodes[n]->Coords[c].bufX; bufferY=Nodes[n]->Coords[c].bufY; angle=StartAngle + double(bufferX) * AngleIncr + xInc[bufferY]; double xb=radius * sin(angle); double xt=topradius * sin(angle); double yb = ybot - perspective * radius * cos(angle); double yt = ytop - perspective * topradius * cos(angle); double posOnString = 0.5; if (BufferHt > 1) { posOnString = yPos[bufferY]/(double)(BufferHt-1.0); } Nodes[n]->Coords[c].screenX = xb + (xt - xb) * posOnString; Nodes[n]->Coords[c].screenY = yb + (yt - yb) * posOnString - ((double)RenderHt)/2.0; } } } else { double treeScale = degrees == -1 ? 5.0 : 4.0; double botWid = BufferWi * treeScale; RenderHt=BufferHt * 2.0; RenderWi=(botWid + 2); double offset = 0.5; size_t NodeCount=GetNodeCount(); for(size_t n=0; n<NodeCount; n++) { size_t CoordCount=GetCoordCount(n); if (degrees == -1) { for(size_t c=0; c < CoordCount; c++) { bufferX=Nodes[n]->Coords[c].bufX; bufferY=Nodes[n]->Coords[c].bufY; double xt = (bufferX + offset - BufferWi/2.0) * 0.9; double xb = (bufferX + offset - BufferWi/2.0) * treeScale; double h = std::sqrt(RenderHt * RenderHt + (xt - xb)*(xt - xb)); double posOnString = 0.5; if (BufferHt > 1) { posOnString = (bufferY/(double)(BufferHt-1.0)); } double newh = RenderHt * posOnString; Nodes[n]->Coords[c].screenX = xb + (xt - xb) * posOnString; Nodes[n]->Coords[c].screenY = RenderHt * newh / h - ((double)RenderHt)/2.0; posOnString = 0; if (BufferHt > 1) { posOnString = ((bufferY - 0.33)/(double)(BufferHt-1.0)); } newh = RenderHt * posOnString; Nodes[n]->Coords.push_back(Nodes[n]->Coords[c]); Nodes[n]->Coords.back().screenX = xb + (xt - xb) * posOnString; Nodes[n]->Coords.back().screenY = RenderHt * newh / h - ((double)RenderHt)/2.0; posOnString = 1; if (BufferHt > 1) { posOnString = ((bufferY + 0.33)/(double)(BufferHt-1.0)); } newh = RenderHt * posOnString; Nodes[n]->Coords.push_back(Nodes[n]->Coords[c]); Nodes[n]->Coords.back().screenX = xb + (xt - xb) * posOnString; Nodes[n]->Coords.back().screenY = RenderHt * newh / h - ((double)RenderHt)/2.0; } } else { for(size_t c=0; c < CoordCount; c++) { bufferX=Nodes[n]->Coords[c].bufX; bufferY=Nodes[n]->Coords[c].bufY; double xt = (bufferX + offset - BufferWi/2.0) * 0.9; double xb = (bufferX + offset - BufferWi/2.0) * treeScale; double posOnString = 0.5; if (BufferHt > 1) { posOnString = (bufferY/(double)(BufferHt-1.0)); } Nodes[n]->Coords[c].screenX = xb + (xt - xb) * posOnString; Nodes[n]->Coords[c].screenY = RenderHt * posOnString - ((double)RenderHt)/2.0; } } } } screenLocation.SetRenderSize(RenderWi, RenderHt); }