How can I draw a perfect donut with inner and outer radius with a CAShapeLayer?
Or better to ask: How to draw a Circle with a hole in it, so that the stroke is two seperate lines. One on the inner and one on the outer circle. All I've achieved so far is that the stroke of the inner and outer circle is connected, which I want to avoid:
CGPathAddArc( _path, NULL, _center.x, _center.y, 100, 0, M_PI * 2.0f, NO);
CGPathRef temp = CGPathCreateCopyByStrokingPath(_path, &_transform, 10, kCGLineCapButt, kCGLineJoinMiter, 10);
_path = CGPathCreateMutableCopy(temp);
You can create a path that has two circles (the outer and the inner radius) added to it. Then you can set the fillMode of the shape layer to kCAFillRuleEvenOdd.
Related
So my problem involves trying to keep points drawn into a rectangle at the same position of the rectangle while an outer container box is scaled to any size.
The rectangle containing the points will keep its aspect ratio while it grows and shrinks in the center of the outer box.
I'm able to keep the inner box's aspect ratio constant but am having problems drawing the points in the correct place when scaling the outer box. Here's an example of my problem.
I'd like the point to stay on the same spot the picture is no matter how the outer box is scaled. The coordinate system has 0,0 as the topleft of the outer box and the inner box is centered using offsets allowing the inner box to be big as possible while maintaining its aspect ratio, however I'm stuck on getting points I add to maintain their position in the box. Here's a look at what I think I should be doing:
void PointsHandler::updatePoints()
{
double imgRatio = boxSize.width() / boxSize.height();
double oldXOffset = (oldContainerSize.width() - oldBoxSize.width()) / 2;
double oldYOffset = (oldContainerSize.height() - oldBoxSize.height()) / 2;
double newXOffset = (containerSize.width() - boxSize.width()) / 2;
double newYOffset = (containerSize.height() - boxSize.height()) / 2;
for(int i = 0; i < points.size(); i++){
double newX = ((points[i].x() - oldXOffset) + newXOffset) * boxRatio;
double newY = ((points[i].y() - oldYOffset) + newYOffset) * boxRatio;
points.replace(i, Point(newX, newY));
}
}
The requested transformations are only translations and scale.
To preserve the original aspect ratio of the inner box, the scale factor must be the same for the x and the y axes. To choose which one to apply, the user should compare the ratio between the width and the height of the new outer box with the aspect ratio of the old inner box. If it's lower, the scale should be the ratio between the new width and the old one, otherwise the ratio between the heights.
To respect the correct order of the transformations, you need to first apply a translation of the points so that the old center of inner box coincides with the origin of the axes (the top left corner of the outer box, apparently), then scale the points and finally translate back to the new center of the outer box. That's not what the posted code attempts to do, because it seems that the scale is applied last.
I'm making a sniper shooter arcade style game in Gamemaker Studio 2 and I want the position of targets outside of the viewport to be pointed to by chevrons that move along the circumference of the scope when it moves. I am using trig techniques to determine the coordinates but the chevron is jumping around and doesn't seem to be pointing to the target. I have the code broken into two: the code to determine the coordinates in the step event of the enemies class (the objects that will be pointed to) and a draw event in the same class. Additionally, when I try to rotate the chevron so it also points to the enemy, it doesn't draw at all.
Here's the coordinate algorithm and the code to draw the chevrons, respectively
//determine the angle the target makes with the player
delta_x = abs(ObjectPlayer.x - x); //x axis displacement
delta_y = abs(ObjectPlayer.y - y); //y axis displacement
angle = arctan2(delta_y,delta_x); //angle in radians
angle *= 180/pi //angle in radians
//Determine the direction based on the larger dimension and
largest_distance = max(x,y);
plusOrMinus = (largest_distance == x)?
sign(ObjectPlayer.x-x) : sign(ObjectPlayer.y-y);
//define the chevron coordinates
chevron_x = ObjectPlayer.x + plusOrMinus*(cos(angle) + 20);
chevron_y = ObjectPlayer.y + plusOrMinus*(sign(angle) + 20);
The drawing code
if(object_exists(ObjectEnemy)){
draw_text(ObjectPlayer.x, ObjectPlayer.y-10,string(angle));
draw_sprite(Spr_Chevron,-1,chevron_x,chevron_y);
//sSpr_Chevron.image_angle = angle;
}
Your current code is slightly more complex that it needs to be for this, if you want to draw chevrons pointing towards all enemies, you might as well do that on spot in Draw. And use degree-based functions if you're going to need degrees for drawing anyway
var px = ObjectPlayer.x;
var py = ObjectPlayer.y;
with (ObjectEnemy) {
var angle = point_direction(px, py, x, y);
var chevron_x = px + lengthdir_x(20, angle);
var chevron_y = py + lengthdir_y(20, angle);
draw_sprite_ext(Spr_Chevron, -1, chevron_x, chevron_y, 1, 1, angle, c_white, 1);
}
(also see: an almost-decade old blog post of mine about doing this while clamping to screen edges instead)
Specific problems with your existing code are:
Using a single-axis plusOrMinus with two axes
Adding 20 to sine/cosine instead of multiplying them by it
Trying to apply an angle to sSpr_Chevron (?) instead of using draw_sprite_ext to draw a rotated sprite.
Calculating largest_distance based on executing instance's X/Y instead of delta X/Y.
I need to draw a circle with QPainter. When I used drawEllipse function like :
void UserClass::Draw(QPainter &painter) {
painter.save();
painter.setBrush( GetColor() );
QPoint centerPosition = GetCenterPosition();
painter.drawEllipse( centerPosition, m_CircleOuterRadius, m_CircleOuterRadius);
painter.setBrush(QColor(0, 0, 0, 0));
painter.drawEllipse( centerPosition, m_CircleInnerRadius, m_CircleInnerRadius);
painter.restore();
}
Unfortunately result is not what I desired. I want to have inner circle not be filled. That is why I put alpha value as zero but ofcourse it didn't work. How can I have a circle which is not until a certain radius with qt ?
You should create a QPainterPath then add the two circles to it via addEllipse(), the outer first, then the inner. This will effectively give you a shape that is the outer circle with the inner circle punched as a hole.
Then you fill the painter path with a green brush, which will result in a hollow ring. Afterwards, if you want the white outlines, you can stroke the path with a white pen as well.
Also note that the painter path can be created only once and stored for reuse instead of creating it anew every time you redraw.
Context
I try to draw pie chart for statistic in my game. I'm using Cocos2d-x ver.3.8.1. Size of the game is important, so I won't to use third-party frameworks to create pie charts.
Problem
I could not find any suitable method in Cocos2d-x for drawing part of the circle.
I tried to do
I tried to find a solution to this problem in Internet, but without success.
As is known, sector of a circle = triangle + segment. So, I tried to use the method drawSegment() from DrawNode also.
Although it has parameter radius ("The segment radius" written in API reference), radius affects only the thickness of the line.
drawSegment() method draw a simple line, the thickness of which is set by a method call.
Question
Please prompt me, how can I draw a segment or a sector of a circle in Cocos2d-x?
Any advice will be appreciated, thanks.
I think the one of the ways to draw a sector of a circle in Cocos2d-X is the way to use drawPolygon on DrawNode. I wrote little sample.
void drawSector(cocos2d::DrawNode* node, cocos2d::Vec2 origin, float radius, float angle_degree,
cocos2d::Color4F fillColor, float borderWidth, cocos2d::Color4F bordercolor,
unsigned int num_of_points = 100)
{
if (!node)
{
return;
}
const cocos2d::Vec2 start = origin + cocos2d::Vec2{radius, 0};
const auto angle_step = 2 * M_PI * angle_degree / 360.f / num_of_points;
std::vector<cocos2d::Point> circle;
circle.emplace_back(origin);
for (int i = 0; i <= num_of_points; i++)
{
auto rads = angle_step * i;
auto x = origin.x + radius * cosf(rads);
auto y = origin.y + radius * sinf(rads);
circle.emplace_back(x, y);
}
node->drawPolygon(circle.data(), circle.size(), fillColor, borderWidth, bordercolor);
}
This is the function to calculate the position of edge point of circle and draw polygon. If you want to use it, you need to call like following,
auto canvas = DrawNode::create();
drawSector(canvas, cocos2d::Vec2(400, 400), 100, 60, cocos2d::Color4F::GREEN, 2, cocos2d::Color4F::BLUE, 100);
this->addChild(triangle);
The result would be like this. I think the code will help your problem.
I'm attempting to create a "U" shape in Box2d (in Cocos2d) by joining 3 rectangles like so: |_|
It sounds like joints are not the correct solution here since I don't want any movement so I have created a main body which is the middle bit and 2 fixtures for the sides. I've added the two sides to the middle bit like this:
mainBody->CreateFixture(&leftFixtureDef);
mainBody->CreateFixture(&rightFixtureDef);
This works, however both side fixtures get added to the center of the mainBody. I can't seem to work out how to position the fixtures relative to the main body. Attaching a sprite/node to the fixture and changing the position doesn't seem to make a difference.
Any ideas?
Many thanks.
it's the property of a shape. I did not find such property for b2CircleShape, but for b2PolygonShape has m_centroid paramter - it's the shape center coordinates relative to the body. Specify it to have a valid position of a shape.
For b2PolyganShape there is a method setAsBox(w, h) but alos there is more complex one:
setAsBox(float32 width, float32 height, const b2Vec2 ¢er, float32 rotation)
Use this method or specify the centroid manualy.
Here is the code for the U shape
b2BodyDef bDef;
bDef.type = b2_dynamicBody;
bDef.position = b2Vec2(0, 0);
b2Body *body = world_->CreateBody(&bDef);
b2PolygonShape shape;
const float32 density = 10;
shape.SetAsBox(1, 0.1);
body->CreateFixture(&shape, density);
shape.SetAsBox(0.1, 1, b2Vec2(-1 + 0.1, 1), 0);
body->CreateFixture(&shape, density);
shape.SetAsBox(0.1, 1, b2Vec2(1 - 0.1, 1), 0);
body->CreateFixture(&shape, density);