// general purpose facet-drop which calls xy_normal_length(), normal_length(), // and center_height() on the subclass bool MillingCutter::facetDrop(CLPoint &cl, const Triangle &t) const { // Drop cutter at (cl.x, cl.y) against facet of Triangle t Point normal = t.upNormal(); // facet surface normal if ( isZero_tol( normal.z ) ) // vertical surface return false; //can't drop against vertical surface assert( isPositive( normal.z ) ); if ( ( isZero_tol(normal.x) ) && ( isZero_tol(normal.y) ) ) { // horizontal plane special case CCPoint cc_tmp( cl.x, cl.y, t.p[0].z, FACET); return cl.liftZ_if_inFacet(cc_tmp.z, cc_tmp, t); } else { // general case // plane containing facet: a*x + b*y + c*z + d = 0, so // d = -a*x - b*y - c*z, where (a,b,c) = surface normal double d = - normal.dot(t.p[0]); normal.normalize(); // make length of normal == 1.0 Point xyNormal( normal.x, normal.y, 0.0); xyNormal.xyNormalize(); // define the radiusvector which points from the cc-point to the cutter-center Point radiusvector = this->xy_normal_length*xyNormal + this->normal_length*normal; CCPoint cc_tmp = cl - radiusvector; // NOTE xy-coords right, z-coord is not. cc_tmp.z = (1.0/normal.z)*(-d-normal.x*cc_tmp.x-normal.y*cc_tmp.y); // cc-point lies in the plane. cc_tmp.type = FACET; double tip_z = cc_tmp.z + radiusvector.z - this->center_height; return cl.liftZ_if_inFacet(tip_z, cc_tmp, t); } }
// general purpose vertex-drop which delegates to this->height(r) of subclass bool MillingCutter::vertexDrop(CLPoint &cl, const Triangle &t) const { bool result = false; BOOST_FOREACH( const Point& p, t.p) { // test each vertex of triangle double q = cl.xyDistance(p); // distance in XY-plane from cl to p if ( q <= radius ) { // p is inside the cutter CCPoint cc_tmp(p, VERTEX); if ( cl.liftZ( p.z - this->height(q), cc_tmp ) ) result = true; } } return result; }
// "dual" edge-drop problems // cylinder: zero diam edge/ellipse, r-radius cylinder, find r-offset == cl (ITO surface XY-slice is a circle) // sphere: zero diam cylinder. ellipse around edge, find offset == cl (ITO surface slice is ellipse) (?) // toroid: radius2 diam edge, radius1 cylinder, find radius1-offset-ellipse=cl (ITO surf slice is offset ellipse) (this is the offset-ellipse problem) // cone: ??? (how is this an ellipse??) bool MillingCutter::singleEdgeDrop(CLPoint& cl, const Point& p1, const Point& p2, double d) const { Point v = p2 - p1; // vector along edge, from p1 -> p2 Point vxy( v.x, v.y, 0.0); vxy.xyNormalize(); // normalized XY edge vector // figure out u-coordinates of p1 and p2 (i.e. x-coord in the rotated system) Point sc = cl.xyClosestPoint( p1, p2 ); assert( ( (cl-sc).xyNorm() - d ) < 1E-6 ); // edge endpoints in the new coordinate system, in these coordinates, CL is at origo Point up1( (p1-sc).dot(vxy) , d, p1.z); // d, distance to line, is the y-coord in the rotated system Point up2( (p2-sc).dot(vxy) , d, p2.z); CC_CLZ_Pair contact = this->singleEdgeDropCanonical( up1, up2 ); // the subclass handles this CCPoint cc_tmp( sc + contact.first * vxy, EDGE); // translate back into original coord-system cc_tmp.z_projectOntoEdge(p1,p2); return cl.liftZ_if_InsidePoints( contact.second , cc_tmp , p1, p2); }
CLPoint CCopasiSpringLayout::borderProjection(CLGraphicalObject* go, const CLPoint & p, double d) { CLPoint center = go->getBoundingBox().getCenter(); CLPoint diff = p - center; CLPoint ret; if (fabs(diff.getX()) * (fabs(go->getHeight()) * 0.5 + d) > fabs(diff.getY()) * (fabs(go->getWidth()) * 0.5 + d)) { double f = (fabs(go->getWidth()) * 0.5 + d) / fabs(diff.getX()); ret = center + diff * f; } else { double f = (fabs(go->getHeight()) * 0.5 + d) / fabs(diff.getY()); ret = center + diff * f; } return ret; }
// because this checks for contact with both the tip and the circular edge it is hard to move to the base-class // we either hit the tip, when the slope of the plane is smaller than angle // or when the slope is steep, the circular edge between the cone and the cylindrical shaft bool ConeCutter::facetDrop(CLPoint &cl, const Triangle &t) const { bool result = false; Point normal = t.upNormal(); // facet surface normal if ( isZero_tol( normal.z ) ) // vertical surface return false; //can't drop against vertical surface if ( (isZero_tol(normal.x)) && (isZero_tol(normal.y)) ) { // horizontal plane special case CCPoint cc_tmp( cl.x, cl.y, t.p[0].z, FACET_TIP ); // so any vertex is at the correct height return cl.liftZ_if_inFacet(cc_tmp.z, cc_tmp, t); } else { // define plane containing facet // a*x + b*y + c*z + d = 0, so // d = -a*x - b*y - c*z, where (a,b,c) = surface normal double a = normal.x; double b = normal.y; double c = normal.z; double d = - normal.dot(t.p[0]); normal.xyNormalize(); // make xy length of normal == 1.0 // cylindrical contact point case // find the xy-coordinates of the cc-point CCPoint cyl_cc_tmp = cl - radius*normal; cyl_cc_tmp.z = (1.0/c)*(-d-a*cyl_cc_tmp.x-b*cyl_cc_tmp.y); double cyl_cl_z = cyl_cc_tmp.z - length; // tip positioned here cyl_cc_tmp.type = FACET_CYL; // tip contact with facet CCPoint tip_cc_tmp(cl.x,cl.y,0.0); tip_cc_tmp.z = (1.0/c)*(-d-a*tip_cc_tmp.x-b*tip_cc_tmp.y); double tip_cl_z = tip_cc_tmp.z; tip_cc_tmp.type = FACET_TIP; result = result || cl.liftZ_if_inFacet( tip_cl_z, tip_cc_tmp, t); result = result || cl.liftZ_if_inFacet( cyl_cl_z, cyl_cc_tmp, t); return result; } }
// call vertex, facet, and edge drop methods on input Triangle t bool MillingCutter::dropCutter(CLPoint &cl, const Triangle &t) const { bool facet, vertex, edge; /* // alternative ordering of the tests: if (cl.below(t)) vertexDrop(cl,t); // optimisation: if we are now above the triangle we don't need facet and edge if ( cl.below(t) ) { facetDrop(cl,t); edgeDrop(cl,t); }*/ if (cl.below(t)) { facet = facetDrop(cl,t); // if we make contact with the facet... if (!facet) { // ...then we will not hit an edge/vertex, so don't check for that vertex = vertexDrop(cl,t); if ( cl.below(t) ) { edge = edgeDrop(cl,t); } } } return ( facet || vertex || edge ); }
// call vertex, facet, and edge drop methods on input Triangle t bool MillingCutter::dropCutter(CLPoint &cl, const Triangle &t) const { bool facet, vertex, edge; /* // alternative ordering of the tests: if (cl.below(t)) vertexDrop(cl,t); // optimisation: if we are now above the triangle we don't need facet and edge if ( cl.below(t) ) { facetDrop(cl,t); edgeDrop(cl,t); }*/ if (cl.below(t)) { facet = facetDrop(cl,t); if (!facet) { vertex = vertexDrop(cl,t); if ( cl.below(t) ) { edge = edgeDrop(cl,t); } } } return ( facet || vertex || edge ); }
// edge-drop function which calls the sub-class MillingCutter::singleEdgeDrop on each // edge of the input Triangle t. bool MillingCutter::edgeDrop(CLPoint &cl, const Triangle &t) const { bool result = false; for (int n=0;n<3;n++) { // loop through all three edges int start=n; // index of the start-point of the edge int end=(n+1)%3; // index of the end-point of the edge const Point p1 = t.p[start]; const Point p2 = t.p[end]; if ( !isZero_tol( p1.x - p2.x) || !isZero_tol( p1.y - p2.y) ) { const double d = cl.xyDistanceToLine(p1,p2); if (d<=radius) // potential contact with edge if ( this->singleEdgeDrop(cl,p1,p2,d) ) result=true; } } return result; }
bool CompositeCutter::ccValidRadius(unsigned int n, CLPoint& cl) const { if (cl.cc->type == NONE) return false; double d = cl.xyDistance(*cl.cc); double lolimit; double hilimit; if (n==0) lolimit = - 1E-6; else lolimit = radiusvec[n-1] - 1E-6; hilimit = radiusvec[n]+1e-6; // FIXME: really ugly solution this one... if (d<lolimit) return false; else if (d>hilimit) return false; else return true; }
// use OpenMP to share work between threads void PointDropCutter::pointDropCutter1(CLPoint& clp) { nCalls = 0; int calls=0; std::list<Triangle>* tris; //tris=new std::list<Triangle>(); tris = root->search_cutter_overlap( cutter, &clp ); std::list<Triangle>::iterator it; for( it=tris->begin(); it!=tris->end() ; ++it) { // loop over found triangles if ( cutter->overlaps(clp,*it) ) { // cutter overlap triangle? check if (clp.below(*it)) { cutter->dropCutter(clp,*it); ++calls; } } } delete( tris ); nCalls = calls; return; }
/* * Copyright 2010-2011 Anders Wallin (anders.e.e.wallin "at" gmail.com) * * This file is part of OpenCAMlib. * * OpenCAMlib is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenCAMlib is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenCAMlib. If not, see <http://www.gnu.org/licenses/>. */ #include <iostream> #include <sstream> #include <string> #include "compositecutter.hpp" #include "numeric.hpp" #include "cylcutter.hpp" #include "ballcutter.hpp" #include "bullcutter.hpp" #include "conecutter.hpp" namespace ocl { CompositeCutter::CompositeCutter() { radiusvec = std::vector<double>(); cutter = std::vector<MillingCutter*>(); radius=0; diameter=0; } // add cutter c which is valid until radius=r and height=z with z-offset zoff void CompositeCutter::addCutter(MillingCutter& c, double r, double h, double zoff) { radiusvec.push_back(r); heightvec.push_back(h); cutter.push_back(&c); zoffset.push_back(zoff); if (r>radius) { radius = r; diameter = 2*r; } // update length also? } // this allows vertexDrop in the base-class to work as for other cutters double CompositeCutter::height(double r) const { unsigned int idx = radius_to_index(r); return cutter[idx]->height(r) + zoffset[idx]; } // return the width of the cutter at height h. double CompositeCutter::width(double h) const { unsigned int idx = height_to_index(h); // std::cout << "CompositeCutter::width( " << h << " ) idx=" << idx << " zoffset= " << zoffset[idx] << "\n"; // std::cout << " width = " << cutter[idx]->width( h - zoffset[idx] ) << "\n"; return cutter[idx]->width( h - zoffset[idx] ); } unsigned int CompositeCutter::height_to_index(double h) const { for (unsigned int n=0; n<cutter.size(); ++n) { if ( validHeight(n,h) ) return n; } // return the last cutter if we get here... return cutter.size()-1; std::cout << " Error, height= " << h << " has no index \n"; assert(0); return 0; } unsigned int CompositeCutter::radius_to_index(double r) const { for (unsigned int n=0; n<cutter.size(); ++n) { if ( validRadius(n,r) ) return n; } assert(0); return 0; } bool CompositeCutter::ccValidRadius(unsigned int n, CLPoint& cl) const { if (cl.cc->type == NONE) return false; double d = cl.xyDistance(*cl.cc); double lolimit; double hilimit; if (n==0) lolimit = - 1E-6; else lolimit = radiusvec[n-1] - 1E-6; hilimit = radiusvec[n]+1e-6; // FIXME: really ugly solution this one... if (d<lolimit) return false; else if (d>hilimit) return false; else return true; } bool CompositeCutter::ccValidHeight(unsigned int n, CCPoint& cc, const Fiber& f) const { //if ( ((cc.z-f.p1.z) >= 0.0) && (n == height_to_index(cc.z-f.p1.z)) ) if ( n == height_to_index(cc.z-f.p1.z) ) return true; else return false; } // return true if height h belongs to cutter n bool CompositeCutter::validHeight(unsigned int n, double h) const { double lolimit, hilimit; if (n==0) lolimit = -1E-6; else lolimit = heightvec[n-1] - 1E-6; hilimit = heightvec[n]+1e-6; // FIXME: really ugly solution this one... if ( (lolimit<=h) ) if (h<=hilimit) return true; return false; } bool CompositeCutter::validRadius(unsigned int n, double r) const { assert( r >= 0.0 ); double lolimit, hilimit; if (n==0) lolimit = -1E-6; else lolimit = radiusvec[n-1] - 1E-6; hilimit = radiusvec[n]+1e-6; // FIXME: really ugly solution this one... if ( (lolimit<=r) ) if (r<=hilimit) return true; return false; } //******** facetDrop ********************** */ // call facetDrop on each cutter and pick a valid cc-point bool CompositeCutter::facetDrop(CLPoint &cl, const Triangle &t) const { bool result = false; for (unsigned int n=0; n<cutter.size(); ++n) { // loop through cutters CLPoint cl_tmp = cl + CLPoint(0,0,zoffset[n]); CCPoint* cc_tmp; if ( cutter[n]->facetDrop(cl_tmp, t) ) { assert( cl_tmp.cc != 0); if ( ccValidRadius(n,cl_tmp) ) { // cc-point is valid cc_tmp = new CCPoint(*cl_tmp.cc); if (cl.liftZ( cl_tmp.z - zoffset[n] )) { // we need to lift the cutter cc_tmp->type = FACET; cl.cc = cc_tmp; result = true; } else { delete cc_tmp; } } } } return result; } //******** edge **************************************************** */ bool CompositeCutter::edgeDrop(CLPoint &cl, const Triangle &t) const { bool result = false; for (unsigned int n=0; n<cutter.size(); ++n) { // loop through cutters CLPoint cl_tmp = cl + Point(0,0,zoffset[n]); CCPoint* cc_tmp; if ( cutter[n]->edgeDrop(cl_tmp,t) ) { // drop sub-cutter against edge if ( ccValidRadius(n,cl_tmp) ) { // check if cc-point is valid cc_tmp = new CCPoint(*cl_tmp.cc); if (cl.liftZ( cl_tmp.z - zoffset[n] ) ) { // we need to lift the cutter cc_tmp->type = EDGE; cl.cc = cc_tmp; result = true; } else { delete cc_tmp; } } } } return result; }
// call facetDrop on each cutter and pick a valid cc-point bool CompositeCutter::facetDrop(CLPoint &cl, const Triangle &t) const { bool result = false; for (unsigned int n=0; n<cutter.size(); ++n) { // loop through cutters CLPoint cl_tmp = cl + CLPoint(0,0,zoffset[n]); CCPoint* cc_tmp; if ( cutter[n]->facetDrop(cl_tmp, t) ) { assert( cl_tmp.cc != 0); if ( ccValidRadius(n,cl_tmp) ) { // cc-point is valid cc_tmp = new CCPoint(*cl_tmp.cc); if (cl.liftZ( cl_tmp.z - zoffset[n] )) { // we need to lift the cutter cc_tmp->type = FACET; cl.cc = cc_tmp; result = true; } else { delete cc_tmp; } } } } return result; }
void CQGLViewport::updateScrollbars() { // reset the scollbar range // TODO check te setting for the scroll range since there seem to be some // error messages // disconnect the scrollbar listeners and handle the update so that the GL // window is only redrawn once double zoom = this->mpNetworkPainter->getZoomFactor(); CLPoint max = this->mpNetworkPainter->getGraphMax(); CLPoint min = this->mpNetworkPainter->getGraphMin(); double graphWidth = (max.getX() - min.getX()) * zoom; double graphHeight = (max.getY() - min.getY()) * zoom; double rectangleHeight = this->contentsRect().height(); double rectangleWidth = this->contentsRect().width(); if (graphHeight < rectangleHeight) { this->mpVerticalScrollbar->hide(); this->mpVerticalScrollbar->setValue(0); } else { this->mpVerticalScrollbar->setPageStep(rectangleHeight); this->mpVerticalScrollbar->setRange(0, (unsigned int)(graphHeight - rectangleHeight)); this->mpVerticalScrollbar->show(); this->mpNetworkPainter->update(); } if (graphWidth < rectangleWidth) { this->mpHorizontalScrollbar->hide(); this->mpHorizontalScrollbar->setValue(0); } else { this->mpHorizontalScrollbar->setPageStep(rectangleWidth); this->mpHorizontalScrollbar->setRange(0, (unsigned int)(graphWidth - rectangleWidth)); this->mpHorizontalScrollbar->show(); this->mpNetworkPainter->update(); } }
void CCopasiSpringLayout::finalizeState() { unsigned int i; //update the positions of the dependent glyphs //this can be done here since we assume that those glyphs //do not affect the layout. std::vector<CoordinateRelation>::const_iterator it, itEnd = mFixedRelations.end(); for (it = mFixedRelations.begin(); it != itEnd; ++it) it->target->setPosition(it->source->getPosition() + it->diff); //for now, only create curves for the reaction glyphs for (i = 0; i < mpLayout->getListOfReactionGlyphs().size() ; ++i) { CLReactionGlyph* pRG = mpLayout->getListOfReactionGlyphs()[i]; //Determine the average position of substrates and products, giving less weight to side reactants CLPoint s, p; double s_c = 0; double p_c = 0; unsigned int j, jmax = pRG->getListOfMetabReferenceGlyphs().size(); for (j = 0; j < jmax; ++j) { if (pRG->getListOfMetabReferenceGlyphs()[j]->getRole() == CLMetabReferenceGlyph::SUBSTRATE) { s_c += 1.0; s = s + pRG->getListOfMetabReferenceGlyphs()[j]->getMetabGlyph()->getBoundingBox().getCenter(); } if (pRG->getListOfMetabReferenceGlyphs()[j]->getRole() == CLMetabReferenceGlyph::SIDESUBSTRATE) { s_c += 0.1; s = s + pRG->getListOfMetabReferenceGlyphs()[j]->getMetabGlyph()->getBoundingBox().getCenter() * 0.1; } if (pRG->getListOfMetabReferenceGlyphs()[j]->getRole() == CLMetabReferenceGlyph::PRODUCT) { p_c += 1.0; p = p + pRG->getListOfMetabReferenceGlyphs()[j]->getMetabGlyph()->getBoundingBox().getCenter(); } if (pRG->getListOfMetabReferenceGlyphs()[j]->getRole() == CLMetabReferenceGlyph::SIDEPRODUCT) { p_c += 0.1; p = p + pRG->getListOfMetabReferenceGlyphs()[j]->getMetabGlyph()->getBoundingBox().getCenter() * 0.1; } } if (s_c > 0) s = s * (1 / s_c); else s = pRG->getPosition(); if (p_c > 0) p = p * (1 / p_c); else p = pRG->getPosition(); CLPoint dir = p - s; //overall direction of reaction if (dir.getX() == 0 && dir.getY() == 0) dir = CLPoint(1, 0); CLPoint ortho_dir = CLPoint(dir.getY(), -dir.getX()); ortho_dir.scale(1 / sqrt(pow(ortho_dir.getX(), 2) + pow(ortho_dir.getY(), 2))); CLPoint reaction_s = pRG->getPosition() - (dir * 0.05); CLPoint reaction_p = pRG->getPosition() + (dir * 0.05); CLPoint reaction_m1 = pRG->getPosition() + ortho_dir * 10; CLPoint reaction_m2 = pRG->getPosition() - ortho_dir * 10; pRG->getCurve().clear(); pRG->getCurve().addCurveSegment(CLLineSegment(reaction_s, reaction_p)); for (j = 0; j < jmax; ++j) { //here we need to generate the curves for the MetabReferenceGlyphs. //we will need to consider the size of the glyphs, role of the metab in the reaction, etc. //For now, only a primitive implementation: TODO: improve CLMetabReferenceGlyph* pMRG = pRG->getListOfMetabReferenceGlyphs()[j]; double direction; double modifierLength = -0.2; switch (pMRG->getRole()) { case CLMetabReferenceGlyph::SUBSTRATE : case CLMetabReferenceGlyph::SIDESUBSTRATE : { direction = -0.1; CLPoint metabPoint = borderProjection(pMRG->getMetabGlyph(), reaction_s + dir * direction /*(modifierLength * 1.5)*/, 5); pMRG->getCurve().clear(); pMRG->getCurve().addCurveSegment(CLLineSegment(reaction_s, metabPoint, reaction_s + dir * direction, (reaction_s + dir * (direction * 1.5) + metabPoint) * 0.5)); } break; case CLMetabReferenceGlyph::PRODUCT : case CLMetabReferenceGlyph::SIDEPRODUCT : { direction = 0.1; CLPoint metabPoint = borderProjection(pMRG->getMetabGlyph(), reaction_p + dir * direction /*(modifierLength * 1.5)*/, 5); pMRG->getCurve().clear(); pMRG->getCurve().addCurveSegment(CLLineSegment(reaction_p, metabPoint, reaction_p + dir * direction, (reaction_p + dir * (direction * 1.5) + metabPoint) * 0.5)); } break; default: { CLPoint reactionPoint; if (ortho_dir.dot(pRG->getPosition() - pMRG->getMetabGlyph()->getPosition()) < 0) { direction = +10.0; reactionPoint = reaction_m1; } else { direction = -10.0; reactionPoint = reaction_m2; } CLPoint metabPoint = borderProjection(pMRG->getMetabGlyph(), reactionPoint + dir * 0 * direction /*(modifierLength * 1.5)*/, 5); pMRG->getCurve().clear(); pMRG->getCurve().addCurveSegment(CLLineSegment(metabPoint, reactionPoint, (reactionPoint + dir * (0 * direction * 1.5) + metabPoint) * 0.5, reactionPoint + ortho_dir * direction)); } } } } //rearrange the text boxes //TODO //calculate bounding box for the layout, or recenter the layout //for (i = 0; i < mpLayout->getListOfSpeciesGlyphs().size() ; ++i) const CLBoundingBox &bounds = mpLayout->calculateBoundingBox(); mpLayout->setDimensions(CLDimensions(bounds.getPosition().getX() + bounds.getDimensions().getWidth(), bounds.getPosition().getY() + bounds.getDimensions().getHeight())); }
void CQGLViewport::slotHValueChanged(int value) { CLPoint p = this->mpNetworkPainter->getGraphMin(); double zoom = this->mpNetworkPainter->getZoomFactor(); this->mpNetworkPainter->setCurrentPositionX((double)(p.getX() + value / zoom)); }
void CCopasiSpringLayout::finalizeState() { unsigned int i; //update the positions of the dependent glyphs //this can be done here since we assume that those glyphs //do not affect the layout. updateFixedRelations(); //for now, only create curves for the reaction glyphs for (i = 0; i < mpLayout->getListOfReactionGlyphs().size() ; ++i) { CLReactionGlyph* pRG = &mpLayout->getListOfReactionGlyphs()[i]; //Determine the average position of substrates and products, giving less weight to side reactants CLPoint s, p; double s_c = 0; double p_c = 0; unsigned int j, jmax = pRG->getListOfMetabReferenceGlyphs().size(); for (j = 0; j < jmax; ++j) { if (pRG->getListOfMetabReferenceGlyphs()[j].getFunctionalRole() == CLMetabReferenceGlyph::SUBSTRATE) { CLMetabGlyph* metabGlyph = pRG->getListOfMetabReferenceGlyphs()[j].getMetabGlyph(); if (metabGlyph != NULL) { s_c += 1.0; s = s + metabGlyph->getBoundingBox().getCenter(); } } if (pRG->getListOfMetabReferenceGlyphs()[j].getFunctionalRole() == CLMetabReferenceGlyph::SIDESUBSTRATE) { CLMetabGlyph* metabGlyph = pRG->getListOfMetabReferenceGlyphs()[j].getMetabGlyph(); if (metabGlyph != NULL) { s_c += 0.1; s = s + metabGlyph->getBoundingBox().getCenter() * 0.1; } } if (pRG->getListOfMetabReferenceGlyphs()[j].getFunctionalRole() == CLMetabReferenceGlyph::PRODUCT) { CLMetabGlyph* metabGlyph = pRG->getListOfMetabReferenceGlyphs()[j].getMetabGlyph(); if (metabGlyph != NULL) { p_c += 1.0; p = p + metabGlyph->getBoundingBox().getCenter(); } } if (pRG->getListOfMetabReferenceGlyphs()[j].getFunctionalRole() == CLMetabReferenceGlyph::SIDEPRODUCT) { CLMetabGlyph* metabGlyph = pRG->getListOfMetabReferenceGlyphs()[j].getMetabGlyph(); if (metabGlyph != NULL) { p_c += 0.1; p = p + metabGlyph->getBoundingBox().getCenter() * 0.1; } } } CLPoint position = pRG->getPosition(); if (position.getX() == 0 && position.getY() == 0 && pRG->getDimensions().getWidth() == 0 && pRG->getDimensions().getHeight() == 0 && pRG->getCurve().getNumCurveSegments() > 0) { position = pRG->getCurve().getCurveSegments()[0].getStart(); pRG->setPosition(position); } if (s_c > 0) s = s * (1 / s_c); else { s = position; } if (p_c > 0) p = p * (1 / p_c); else p = position; CLPoint dir = p - s; //overall direction of reaction if (dir.getX() == 0 && dir.getY() == 0) dir = CLPoint(1, 0); CLPoint ortho_dir = CLPoint(dir.getY(), -dir.getX()); ortho_dir.scale(1 / sqrt(pow(ortho_dir.getX(), 2) + pow(ortho_dir.getY(), 2))); CLPoint reaction_s = position - (dir * 0.05); CLPoint reaction_p = position + (dir * 0.05); CLPoint reaction_m1 = position + ortho_dir * 10; CLPoint reaction_m2 = position - ortho_dir * 10; pRG->getCurve().clear(); pRG->getCurve().addCurveSegment(CLLineSegment(reaction_s, reaction_p)); for (j = 0; j < jmax; ++j) { //here we need to generate the curves for the MetabReferenceGlyphs. //we will need to consider the size of the glyphs, role of the metab in the reaction, etc. //For now, only a primitive implementation: TODO: improve CLMetabReferenceGlyph* pMRG = &pRG->getListOfMetabReferenceGlyphs()[j]; double direction; //double modifierLength = -0.2; switch (pMRG->getFunctionalRole()) { case CLMetabReferenceGlyph::SUBSTRATE : case CLMetabReferenceGlyph::SIDESUBSTRATE : { direction = -0.1; CLPoint metabPoint = borderProjection(pMRG->getMetabGlyph(), reaction_s + dir * direction /*(modifierLength * 1.5)*/, 5); pMRG->getCurve().clear(); pMRG->getCurve().addCurveSegment(CLLineSegment(reaction_s, metabPoint, reaction_s + dir * direction, (reaction_s + dir * (direction * 1.5) + metabPoint) * 0.5)); } break; case CLMetabReferenceGlyph::PRODUCT : case CLMetabReferenceGlyph::SIDEPRODUCT : { direction = 0.1; CLPoint metabPoint = borderProjection(pMRG->getMetabGlyph(), reaction_p + dir * direction /*(modifierLength * 1.5)*/, 5); pMRG->getCurve().clear(); pMRG->getCurve().addCurveSegment(CLLineSegment(reaction_p, metabPoint, reaction_p + dir * direction, (reaction_p + dir * (direction * 1.5) + metabPoint) * 0.5)); } break; default: { CLPoint reactionPoint; if (pMRG->getMetabGlyph() && ortho_dir.dot(pRG->getPosition() - pMRG->getMetabGlyph()->getPosition()) < 0) { direction = +10.0; reactionPoint = reaction_m1; } else { direction = -10.0; reactionPoint = reaction_m2; } CLPoint metabPoint = borderProjection(pMRG->getMetabGlyph(), reactionPoint + dir * 0 * direction /*(modifierLength * 1.5)*/, 5); pMRG->getCurve().clear(); pMRG->getCurve().addCurveSegment(CLLineSegment(metabPoint, reactionPoint, (reactionPoint + dir * (0 * direction * 1.5) + metabPoint) * 0.5, reactionPoint + ortho_dir * direction)); } } } } //update the curves in the general glyph for (i = 0; i < mpLayout->getListOfGeneralGlyphs().size() ; ++i) { CLGeneralGlyph* pGG = &mpLayout->getListOfGeneralGlyphs()[i]; size_t j; for (j = 0; j < pGG->getListOfReferenceGlyphs().size(); ++j) { CLReferenceGlyph* pRG = &pGG->getListOfReferenceGlyphs()[j]; if (pRG->getCurve().getNumCurveSegments() == 0) continue; CLPoint refPoint = borderProjection(pRG->getTargetGlyph(), pRG->getBoundingBox().getCenter(), 5); pRG->getCurve().clear(); pRG->getCurve().addCurveSegment(CLLineSegment(refPoint, pRG->getBoundingBox().getCenter())); } } //calculate bounding box for the layout, or recenter the layout mpLayout->calculateAndAssignBounds(); }