I have a problem with the containsPoint method. I draw some boxes and connectors between the boxes. The connectors are basicly a single curve, based on a single curveToPoint:controlPoint1:controlPoint2 call. When I now try to select this curve/path with the mouse then this is tricky to do. The containsPoint: method seems to be very sensitive. I tried to draw the line bigger (setLineWidth:), but that doesn't seem to help.
Any ideas what I need to do differently?
For a CGPath, you can always create a closed path which is the contour of the stroked path using:
CGPathRef strokedPath = CGPathCreateCopyByStrokingPath(
path, // your original CGPathRef
NULL, // don't transform
10.0, // lineWidth
kCGLineCapButt, // lineCap (default value)
kCGLineJoinMiter, // lineJoin (default value)
0.0 // miterLimit
);
You can read more about path hit testing here (by Ole Begemann) and here (by Rob Napier).
Thanks to David's answer, I can now provide a full answer.
What I needed were three parts.
Convert the NSBezierPath into a CGPath. This can be done as provided in the Apple Documentation. Or you can use the https://github.com/iccir/XUIKit‎ library, which adds the iPhone framework capabilities to the MacOS frameworks.
Use the CGPathCreateCopyByStrokingPath function as suggested by David.
Convert the new CGPath into a NSBezierPath. David's link to Ole Begemann's block was quite helpful to show how to do it. However, XUIKit is once again a step ahead and provides a +(NSBezierPath) bezierPathWithCGPath: function
The result looks like this.
//con as Connector was the starting point
CGPathRef tapTargetPath = CGPathCreateCopyByStrokingPath(con.CGPath, NULL, 4, kCGLineCapButt, kCGLineJoinBevel, kCGLineJoinMiter );
NSBezierPath * hitPath = [NSBezierPath bezierPathWithCGPath:tapTargetPath];
Related
I have a circle-shaped dynamic body and I need to resize it during the game (It appears like a point, then it grows to a circle and after that it starts moving). How should I do that?
I have an idea - it's to use some animation (Circle has the same radius, but due to animation it looks like the circle grows), but I'm not sure if it's right way or not. (Besides I don't know how to realize it)
For scaling circle, if you are using sprite just scale it sprite.setScale(float), if your sprite is attached to Box2d Circle-shape then get the Body's shape and set the radius
Shape shape = body.getFixture().getShape;
shape.setRadius(radiusValue);
and if you are using ShapeRenderer just multiply the points of ShapeRenderer.
I assume that you are talking about a Box2D body.
It is not possible to change a circle-shaped fixture with Box2D. Box2D is a rigid body simulator. What you would have to do is destroy the fixture and replace it with a smaller/bigger version of the circle. But this will cause a lot of problems, since you cannot destroy a fixture when there is still a contact for example.
It would be better to keep the circle the same size and just simulate a change in size with an animation of a texture on top.
If you cannot simulate that, then maybe try the following approach: Have several versions of that circle in different sizes and keep them on top of each other. Implement a ContactFilter which will only cause contacts for the one circle which is currently "active".
Inside any Object class with box2d, I use the following for dynamic resizing:
public void resize(float newradius) {
this.body.destroyFixture(this.fixture);
fixtureDef.density = (float) (this.mass/(Math.PI*newradius*newradius));
this.radius = newradius;
CircleShape circle = new CircleShape();
circle.setRadius(newradius);
this.fixtureDef.shape = circle;
circle.dispose();
this.fixture = body.createFixture(fixtureDef);
this.fixture.setUserData(this);
}
You can also see the following topic: How to change size after it has been created
I'm fairly new to wxWidgets so please bear with me. Let's say I have a 10Kx10K image and my wxScrolledWindow has a size of 640x480. I load the whole image into a wxBitmap which I use in my paint function.
Now in my OnPaint function I just say
wxPaintDC dc(this);
dc.DrawBitmap(_Bitmap, 0, 0 );
This somewhat works for the first few paints but soon the Window content is out order and artifacts appear. This happens very fast when I move a scroll bar back and forth very quickly.
I use the latest wxWidgets on a Windows 7 machine.
So, how can I improve my painting code?
Many thanks,
Christian
Using a 10000x10000 wxBitmap is a bad idea on its own, it may simply fail to be created on an older system (that's 400MiB of video RAM!). Drawing it entirely is sheer madness.
I don't know where does your data come from but in a typical case of e.g. a map to be shown on screen, you should break it into tiles, convert the tiles that are currently visible on screen to wxBitmap (or several of them) and draw only those.
Then you may optimize your drawing by using double buffering (which is relatively useless under Windows 7 that double buffers everything on its own) and otherwise, but you should be using a reasonably-sized backing store bitmap.
This sounds like something that might be helped by using double buffering.
The first thing to start trying is to replace wxPaintDC with wxBufferedPaintDC
For more suggestions, here is a wiki article on the subject: http://wiki.wxwidgets.org/Flicker-Free_Drawing
As Ravenspoint kindly pointed out, there is an article on wxWidgets' wiki. So according to that article two things need to happen. First override the EVT_ERASE_BACKGROUND with an empty function.
void Canvas::EraseBackground( wxEraseEvent& WXUNUSED(event))
{
}
And second to implement a basic double buffering scheme. Here is how I did it.
void Canvas::OnPaint(wxPaintEvent& WXUNUSED(event))
{
int x, y;
GetViewStart(&x, &y);
wxRect Client_Area = GetClientRect();
int width = Client_Area.width;
int height = Client_Area.height;
wxBitmap Current = _Bitmap.GetSubBitmap(wxRect( x * 10, y * 10, width, height ));
wxPaintDC dc(this);
dc.DrawBitmap(Current, 0, 0, false );
}
My scroll rate for both x and y is set to 10. That's why I multiply the view start coordinates.
Any more insight is very welcome.
Thanks,
Christian
I have a complex UIBezierCurve which I need to draw once with some particular line parameters, and then draw it again as overlay with other line parameters, but also I need the last part of the curve to be slightly shorter than in previous one.
To do this I want to create the curve by addLineToPoint:, moveToPoint: up to the last part, then make a copy of this curve and add the final segments of the line differently in original and copied curves. And then I stroke the original curve, and the copied one.
The problem is that it doesn't work as I expected.
I create a copy of the curve by:
UIBezierPath* copyCurve = [originalCurve copy];
And the drawing which I do in the originalCurve after that, is applied also to the copyCurve, so I can't do independent drawing for any of this curves.
What is the reason for this connection between original and copy and how can I get rid of it?
EDIT 1:
A solution which I've found is to create the copy in the following way:
UIBezierPath* copyCurve=[UIBezierPath bezierPathWithCGPath:CGPathCreateMutableCopy(originalCurve.CGPath)];
Since this works properly, maybe the problem is in the immutability of the copy I get with
[originalCurve copy]
Create a new, identical path by using the CGPath.
path2 = [UIBezierPath bezierPathWithCGPath:path1.CGPath];
The CGPath property docs state that:
This property contains a snapshot of the path at any given point in time. Getting this property returns an immutable path object that you can pass to Core Graphics functions.
copy() works fine for me as of Swift 4.
let copiedPath = originalPath.copy() as! UIBezierPath
copiedPath.addLine(...)
The originalPath does not get modified.
In addition to #jrturton answer :-
Alternatively we can use :-
let path = UIBezierPath(ovalIn: pathRect)
let newPath = path.cgPath.copy(strokingWithWidth: strokeWidth, lineCap: .butt, lineJoin: .miter, miterLimit: 0)
Reference
I am looking for a non private way to find the position of the Cursor or Caret (blinking bar) in a UITextView preferably as a CGPoint.
There may be a question like this already but it does not provide a definitive way for doing it.
And,
I do not mean the NSRange of the selected area.
Just got it in another thread:
Requires iOS 3.2 or later.
CGPoint cursorPosition = [textview caretRectForPosition:textview.selectedTextRange.start].origin;
Remember to check that selectedTextRange is not nil before calling this method. You should also use selectedTextRange.empty to check that it is the cursor position and not the beginning of a text range. So:
if (textview.selectedTextRange.empty) {
// get cursor position and do stuff ...
}
Pixel-Position of Cursor in UITextView
SWIFT 2.1 version:
let cursorPosition = infoTextView.caretRectForPosition( (infoTextView.selectedTextRange?.start)! ).origin
print("cursorPosition:\(cursorPosition)")
There is no such way. Full stop.
The only thing you could do to come close is to in parallel lay out the text using CoreText, there calcualte the text position from a point and apply that on the UITextView. This, however, works with non-0 selections only. In addition CoreText has a different text layout engine, that e.g. supports kerning, which UITextView doesn't. This may result in deviations of the rendered and laid out text and thus give sub-optimal results.
There is absolutely no way to position the caret. This task is even very though if you do use private API. It's just one of the many "just don't" in iOS.
Swift 4 version:
// lets be safe, thus if-let
if let selectedTextRange = textView.selectedTextRange {
let caretPositionRect = textView.caretRect(for: selectedTextRange.start)
let caretOrigin = caretPositionRect.origin
print(">>>>> position rect: \(caretPositionRect), origin: \(caretOrigin)")
}
We simply use textView.selectedTextRange to get selected text range and than textView.caretRect(for:) to get CGRect defining position of the caret.
I'm trying to get my head around using QuartzCore to render semi-complex text/gradient/image UITableViewCell composites. Thankfully, Opacity will let me visually build the view and then spit out source code to drop in to cocoa touch. Trouble is, Opacity assumes the code is running on iOS 4, which is a problem if you want to draw Quartz views on an iPad.
For me, the offending method is CGPathGetPathBoundingBox ... would someone mind pointing me to a suitable alternative or workaround to this (presumably simple) method?
If you care to have some context (no pun intended), here you go:
transform = CGAffineTransformMakeRotation(1.571f);
tempPath = CGPathCreateMutable();
CGPathAddPath(tempPath, &transform, path);
pathBounds = CGPathGetPathBoundingBox(tempPath);
point = pathBounds.origin;
point2 = CGPointMake(CGRectGetMaxX(pathBounds), CGRectGetMinY(pathBounds));
transform = CGAffineTransformInvert(transform);
The alternative is to iterate on the points of the path and note down the leftmost, rightmost, upmost, and downmost co-ordinates of the anchor points yourself, then work out origin and size from those numbers.
You should wrap this in a function, and name it something like MyPathGetPathBoundingBox, and use that until you drop support for iOS 3.x. That will make it easy to switch to CGPathGetPathBoundingBox once you can.