Create bidirectional arrows in JUNG - jung

Is it possible to create bidirectional arrows in JUNG using FRLayout? Ideally, is it possible to have an algorithm that uses these arrows (end-points are arrowheads at both ends) for cases where both a->b and b<-a?
I think it might be related to
Renderer.Edge<String, String> edgeRenderer =
vv.getRenderer().getEdgeRenderer();
but can't see how to get the shapes of the arrowheads

You could do something like this hack to make (in this case) Curved edges overlay each other:
vv.getRenderContext().setEdgeShapeTransformer(new Function<String, Shape> () {
#Override
public Shape apply(String edge) {
Pair<String> endpoints = graph.getEndpoints(edge);
float controlY = 60.f;
// use some hacked 'ordering' of the endpoint nodes so that the curve for A->B is on the same side as the curve from B->A
if (endpoints.getFirst().toString().compareTo(endpoints.getSecond().toString()) < 0) {
controlY *= -1;
}
return new QuadCurve2D.Float(0.0f, 0.0f, 0.5f, controlY, 1.0f, 0.0f);
}
});

If you render the edges as straight lines, then antiparallel edges (a->b and b->a) will look like what you want.
If you look at PluggableRendererDemo you'll see examples of how to change the edge shape:
vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph));
If you actually want to render two separate edges as a single edge, that's going to be more involved; essentially you'd need to hack (or subclass) BasicEdgeRenderer so that it checks for antiparallel edges and treats them differently. To draw the arrows on both ends of an edge, take a look at the code in that class for rendering undirected edges (which can optionally have arrows on both ends).

Related

Qt 5 drawing translate(), rotate(), font fill issues

I'm writing my first Qt 5 application... This uses a third-party map library (QGeoView).
I need to draw an object (something like a stylized airplane) over this map. Following the library coding guidelines, I derived from the base class QGVDrawItem my QGVAirplane.
The airplane class contains heading and position values: such values must be used to draw the airplane on the map (of course in the correct position and with correct heading). The library requires QGVDrawItem derivatives to override three base class methods:
QPainterPath projShape() const;
void projPaint(QPainter* painter);
void onProjection(QGVMap* geoMap)
The first method is used to achieve the area of the map that needs to be updated. The second is the method responsible to draw the object on the map. The third method is needed to reproject the point from the coordinate space on the map (it's not relevant for the solution of my problem).
My code looks like this:
void onProjection(QGVMap* geoMap)
{
QGVDrawItem::onProjection(geoMap);
mProjPoint = geoMap->getProjection()->geoToProj(mPoint);
}
QPainterPath projShape() const
{
QRectF _bounding = createGlyph().boundingRect();
double _size = fmax(_bounding.height(), _bounding.width());
QPainterPath _bounding_path;
_bounding_path.addRect(0,0,_size,_size);
_bounding_path.translate(mProjPoint.x(), mProjPoint.y());
return _bounding_path;
}
// This function creates the path containing the airplane glyph
// along with its label
QPainterPath createGlyph() const
{
QPainterPath _path;
QPolygon _glyph = QPolygon();
_glyph << QPoint(0,6) << QPoint(0,8) << QPoint(14,6) << QPoint(28,8) << QPoint(28,6) << QPoint(14,0);
_path.addPolygon(_glyph);
_path.setFillRule(Qt::FillRule::OddEvenFill);
_path.addText(OFF_X_TEXT, OFF_Y_TEXT, mFont , QString::number(mId));
QTransform _transform;
_transform.rotate(mHeading);
return _transform.map(_path);
}
// This function is the actual painting method
void drawGlyph(QPainter* painter)
{
painter->setRenderHints(QPainter::Antialiasing, true);
painter->setBrush(QBrush(mColor));
painter->setPen(QPen(QBrush(Qt::black), 1));
QPainterPath _path = createGlyph();
painter->translate(mProjPoint.x(), mProjPoint.y());
painter->drawPath(_path);
}
Of course:
mProjPoint is the position of the airplane,
mHeading is the heading (the direction where the airplane is pointing),
mId is a number identifying the airplane (will be displayed as a label under airplane glyph),
mColor is the color assigned to the airplane.
The problem here is the mix of rotation and translation. Transformation: since the object is rotated, projShape() methods return a bounding rectangle that's not fully overlapping the object drawn on the map...
I also suspect that the center of the object is not correctly pointed on mProjPoint. I tried many times trying to translate the bounding rectangle to center the object without luck.
Another minor issue is the fillup of the font... the label under the airplane glyph is not solid, but it is filled with the same color of the airplane.
How can I fix this?
Generically speaking, the general pattern for rotation is to scale about the origin first and then finish with your final translation.
The following is pseudocode, but it illustrates the need to shift your object's origin to (0, 0) prior to doing any rotation or scaling. After the rotate and scale are done, the object can be moved back from (0, 0) back to where it came from. From here, any post-translation step may be applied.
translate( -origin.x, -origin.y );
rotate( angle );
scale( scale.x, scale y);
translate( origin.x, origin.y );
translate( translation.x, translation.y )
I finally managed to achieve the result I meant....
QPainterPath projShape() const
{
QPainterPath _path;
QRectF _glyph_bounds = _path.boundingRect();
QPainterPath _textpath;
_textpath.addText(0, 0, mFont, QString::number(mId));
QRectF _text_bounds = _textpath.boundingRect();
_textpath.translate(_glyph_bounds.width()/2-_text_bounds.width()/2, _glyph_bounds.height()+_text_bounds.height());
_path.addPath(_textpath);
QTransform _transform;
_transform.translate(mProjPoint.x(),mProjPoint.y());
_transform.rotate(360-mHeading);
_transform.translate(-_path.boundingRect().width()/2, -_path.boundingRect().height()/2);
return _transform.map(_path);
}
void projPaint(QPainter* painter)
{
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setRenderHint(QPainter::TextAntialiasing, true);
painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
painter->setRenderHint(QPainter::HighQualityAntialiasing, true);
painter->setBrush(QBrush(mColor));
painter->setPen(QPen(QBrush(Qt::black), 1));
painter->setFont(mFont);
QPainterPath _path = projShape();
painter->drawPath(_path);
}
Unluckly I still suffer the minor issue with text fill mode:
I would like to have a solid black fill for the text instead of the mColor fill I use for the glyph/polygon.

Finding all non overlapping cycles in an undirected graph

I need to find all simple non overlapping cycles on undirected graph. To find all existing cycles I made an Objective-C version of the algorithm that I found here:
Finding all cycles in undirected graphs
#interface HSValue : NSObject
#property (nonatomic, assign) CGPoint point;
#end
#implementation HSValue
#end
#interface CyclesFinder ()
#property (nonatomic, strong) NSMutableArray <NSArray<HSValue *>*> *cycles;
#property (nonatomic, strong) NSArray <NSArray<HSValue*>*> *edges;
#end
#implementation CyclesFinder
-(void)findCyclesInGraph:(NSArray <NSArray<HSValue*>*> *)edges {
self.edges = edges;
for (NSInteger i=0; i < self.edges.count; i++) {
for (NSInteger j=0; j < self.edges[i].count; j++) {
[self findNewCycles:#[self.edges[i][j]]];
}
}
}
-(void)findNewCycles:(NSArray <HSValue *> *)path {
HSValue *startNode = path[0];
HSValue *nextNode;
NSArray <HSValue *> *sub;
for (NSInteger i=0; i < self.edges.count; i++) {
NSArray <HSValue *> *edge = self.edges[i];
if ([edge containsObject:startNode]) {
if ([edge[0] isEqual:startNode]) {
nextNode = edge[1];
}
else {
nextNode = edge[0];
}
}
else {
nextNode = nil;
}
if (![path containsObject:nextNode] && nextNode) {
sub = #[nextNode];
sub = [sub arrayByAddingObjectsFromArray:path];
[self findNewCycles:sub];
}
else if (path.count > 2 && [nextNode isEqual:path.lastObject]) {
if (![self cycleExist:path]) {
[self.cycles addObject:path];
break;
}
}
}
}
-(BOOL)cycleExist:(NSArray <HSValue*> *)path {
path = [path sortedArrayUsingSelector:#selector(compare:)];
for (NSInteger i=0; i < self.cycles.count; i++) {
NSArray <HSValue *> *cycle = [self.cycles[i] sortedArrayUsingSelector:#selector(compare:)];
if ([cycle isEqualToArray:path]) {
return TRUE;
}
}
return FALSE;
}
Above algorithm works fine (even if it is not very efficient) and it finds all the possible cycles from the graph on the attached picture (please see picture below):
A-B-H-G-F-D-E-A (valid)
B-C-I-H-B (valid)
G-H-I-L-K-G (valid)
F-G-K-J-F (valid)
F-G-H-I-L-K-J-F (invalid)
A-B-C-I-H-G-F-D-E-A (invalid)
A-B-C-I-L-K-J-F-D-E-A (invalid)
A-B-C-I-H-G--K-J-F-D-E-A (invalid)
A-B-H-I-L-K-G-F-D-E-A (invalid)
A-B-H-G-K-J-F-D-E-A (invalid)
A-B-C-I-L-K-G-F-D-E-A (invalid)
B-C-I-L-K-G-H-B (invalid)
B-C-I-L-K-J-F-G-H-B (invalid)
However when I run the above algorithm I want to end up with only those cycles that I highlighted with coloured polygons on the left hand side example. What I don't want are the cycles like the one on the right hand side example.
My first thought was that overlapping cycle will be a cycle that includes all the points from any other cycles, but this is not always true. Can someone point me into the right direction? Is it possible to modify the above algorithm so it finds only non-overlapping cycles or if not what should I do after finding all cycles to filter them?
There isn't enough information just in the undirected graph itself to determine which cycles are which. For example, consider that the following 2 diagrams yield identical undirected graphs:
A-----B E-------------F
| | \ /
C-----D \ A-----B /
| | \| |/
E-----F C-----D
But for the diagram on the left, you want the cycles ABDCA and CDFEC, while for the diagram on the right, you want the cycles ABDCA and EFDBACE. Thus the undirected graph inferred from the diagram isn't enough -- you need to somehow incorporate spatial information from the original diagram.
I'm working on this same problem and a lot of your comments were helpful, especially the comment that all edges will have an area on each side. Thus you could say that each edge has a "left area" and a "right area".
You can add all graph edges to a queue in any order. Peek at the first edge, pick its vertex closer to your origin. Move to the neighbor that is the most counter-clockwise. continue this until you have reached your starting vertex. All of these edges bound your first area. I would give it a unique ID and assign it to a "left area" property of those edges.
Peek at the first edge in the queue and check if it has a "left area". If it does check if it has a "right area" if it does not proceed in a clockwise manner and find the right area. If it has both areas assigned dequeue it and grab the next one.
should be O(e+v) so pretty quick, right?
This is a little bit stream of consciousness but I wanted to get it written down. I'll be writing the algorithm for my actual app and I'll make tweaks as I find problems in it.
Of course I'm open to feedback and suggestions :)
I know this question has been 6 years old yet leaving this answer for someone having the same problem in the future.
Key idea
Every edge has exactly two adjacent faces. Actually, every directed edge has exactly one adjacent face.
Construct polygons by choosing most counter-clockwise adjacent edge. Then the polygons with counter-clockwise orientation will be non-overlapping cycle of the graph.
Algorithm overview
For every edges on the graph, find two polygons containing the edge. One for each direction of the edge.
Filter the polygons whose orientation is counter-clockwise.
The resulting polygons are all of non-overlapping cycles in the given graph.
Finding a polygon containing the given edge in a graph
Basically it's choosing the next edge by most counter-clockwise from the current edge until a cycle is created or it hits dead end.
If the end of the cycle equals to the start of the given edge, then the polygon contains the given edge. Else the polygon doesn't contain the given edge, so ignore it.
I'm just posting the entire code of this function. Read it through and I hope you get the idea.
See caveats below for more informations about orientations and vector calculations.
static public <N extends Location> Optional<Polygon<N>> findPolygon(EndpointPair<N> edge, Graph<N> graph) {
if (!edge.isOrdered()) throw new IllegalArgumentException("The starting edge must be ordered.");
if (!graph.hasEdgeConnecting(edge))
throw new IllegalArgumentException("The starting edge must be contained in the graph");
final N start = edge.source();
final MutableGraph<N> polygonGraph = GraphBuilder.directed()
.incidentEdgeOrder(ElementOrder.stable())
.nodeOrder(ElementOrder.insertion())
.build();
// Set the first edge of the polygon.
N source = start;
N target = edge.adjacentNode(source);
// Start adding edges to polygonGraph.
// Until a cycle is created.
while (true) {
// Check if a cycle is created.
if (polygonGraph.nodes().contains(target)) {
// Connect the last edge.
polygonGraph.putEdge(source, target);
break;
}
// Connect the edge.
polygonGraph.putEdge(source, target);
// Find the most counter-clockwise adjacent vertex from the target.
// Then that vertex is the target of the next edge and the target of the current edge is the source of
// the next edge.
Vector base = source.toVector().clone().subtract(target.toVector());
final N finalTarget = target;
Map<N, Double> angles = graph.adjacentNodes(target).stream().collect(Collectors.toMap(
Function.identity(),
node -> {
Vector u = node.toVector().clone().subtract(finalTarget.toVector());
return Vectors.fullAngle(base, u);
}
));
List<N> adjacentNodes = graph.adjacentNodes(target).stream().filter(not(source::equals)).toList();
// Dead end. Failed to create a polygon. Exit.
if (adjacentNodes.isEmpty()) break;
source = target;
target = Collections.max(adjacentNodes, Comparator.comparingDouble(angles::get));
}
// The created polygon doesn't contain the starting edge.
if (!target.equals(start)) {
return Optional.empty();
}
return Optional.of(new Polygon<>(polygonGraph));
}
Identifying the orientation of a polygon
https://www.baeldung.com/cs/list-polygon-points-clockwise
A polygon is counter-clockwise iff its area > 0.
Optimization
The time complexity of the algorithm is O(E^2). (I think)
But you can apply dynamic programming method and it reduces to O(E) (I think)
The idea is that for every directed edge there exists only one matching polygon.
So when you find a polygon, cache every edges of that polygon and you won't have to find that polygon for that edges again.
// This is a pseudo-code
Map<Edge, Polygon> cache = new HashMap<>();
// If the edge is in cache, skip the polygon search.
if (cache.containsKey(edge)) continue;
// When you have found a polygon, cache the edges.
polygon.edges().forEach(edge -> {
cache.put(edge, polygon);
});
You can also pre-determine if a given edge can construct a polygon by looking at the neighbors of the edge.
If any one of the degree of the vertices of the edge is less than 2, meaning that the edge is not connected to other neighbors at both side, it cannot construct a polygon.
So you can skip the polygon search for this edge.
Caveats
Orientations
About the orientation and the related things, although I wrote this article after choose to use counter-clockwise, it seems that it doesn't matter which side you pick to use as long as be consistent for:
The orientation of the polygon ( counter-clockwise / clockwise)
Which adjacent edge to pick to be the next edge of the polygon ( most counter-clockwise / least counter-clockwise ) (or in other words, least clockwise / most clockwise)
Once you choose one of them to be one thing then the other option is automatically determined in order to the algorithm work.
Angle of two edges
You need to convert edges to vectors in order to calculate the angle between them.
Keep in mind that the tail of the vectors have to be the vertex of the angle.
So, if you're getting the angle between Edge(AB) and Edge(BC) then you have to calculate the angle between u = A - B and w = C - B.
Angle of two vectors
Some APIs define the range of the function for getting angle between two vectors as [-PI/2, PI/2].
But you need it to be [0, 2PI] so, you have to convert it.
You can make it [-PI, PI] by using atan2 function.
https://math.stackexchange.com/questions/878785/how-to-find-an-angle-in-range0-360-between-2-vectors
And then add 2 * PI then take mod 2 * PI.
public class Vectors {
static public double fullAngle(#NotNull Vector v, #NotNull Vector u) {
return (Math.atan2(v.det(u), v.dot(u)) + 2 * Math.PI) % (2 * Math.PI);
}
}

Make a line thicker in 3D?

In reference to this question
Drawing a line between two points using SceneKit
I'm drawing a line in 3D and want to make it thicker by using this code
func renderer(aRenderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: NSTimeInterval) {
//Makes the lines thicker
glLineWidth(20)
}
but it doesn't work, iOS 8.2.
Is there another way?
Update
From the docs
https://developer.apple.com/library/prerelease/ios/documentation/SceneKit/Reference/SCNSceneRendererDelegate_Protocol/index.html#//apple_ref/occ/intfm/SCNSceneRendererDelegate/renderer:updateAtTime:
I did add SCNSceneRendererDelegate and a valid line width but still could not get the line width to increase.
You cannot assign any number to glLineWidth().
You can check the range of possible values of glLineWidth()by:
glGetFloatv(GL_LINE_WIDTH_RANGE,sizes);
One crazy idea is to use a cylinder for drawing lines ;). I use it when I want to have nice and controllable lines but I am not aware of a handy OpenGl function to do so.
#G Alexander: here you go my implementation of cylinder. It is a bit tedious but it is what I have at the moment.
If you give me points p0 and p1, Vector normal=(p1-p0).normalize() would be the axis of the cylinder.
pick point p2 that is not on the vector Normal.
q=(p2-p0).normalize();
normal.crossproduct(q)=v0;
normal.crossproduct(v0)=v1;
Having these two vectors you can have circles with any radius that are stacked along the axis of the cylinder using the following function (A cylinder is a stack of circles):
public Circle make_circle(Point center, Vector v0, Vector v1, double radius)
{
Circle c;
for (double i = 0; i < 2 * Math.PI; i += 0.05)
{
Point p = new Point(center + radius * Math.Cos(i) * v0 + radius * Math.Sin(i) * v1);
c.Add(p);
}
return c;
}
You only need to make circles using this function along the axis of the cylinder:
List<Circle> Cylinder = new List<Circle>();
for(double i=0;i<1;i+=0.1)
{
Cylinder.add( make_circle(P0+i*normal, v0, v1,radius);
}
Now you should take two consecutive circles and connect them with quads by sampling uniformly.
I have implemented it this way since I had circles implemented already.
A simpler way to implement is make the circle along the x axis and then rotate and translate it to p0 and make it align with normal or to use gluCylinder if you are the fan of Glu....
Hopefully it works for you.

How can I perform clipping on rotated rectangles?

So I have this Panel class. It's a little like a Window where you can resize, close, add buttons, sliders, etc. Much like the status screen in Morrowind if any of you remember. The behavior I want is that when a sprite is outside of the panel's bounds it doesn't get drawn and if it's partially outside only the part inside gets drawn.
So what it does right now is first get a rectangle that represents the bounds of the panel, and a rectangle for the sprite, it finds the rectangle of intersection between the two then translates that intersection to the local coordinates of the sprite rectangle and uses that for the source rectangle. It works and as clever as I feel the code is I can't shake the feeling that there's a better way to do this. Also, with this set up I cannot utilize a global transformation matrix for my 2D camera, everything in the "world" must be passed a camera argument to draw. Anyway, here's the code I have:
for the Intersection:
public static Rectangle? Intersection(Rectangle rectangle1, Rectangle rectangle2)
{
if (rectangle1.Intersects(rectangle2))
{
if (rectangle1.Contains(rectangle2))
{
return rectangle2;
}
else if (rectangle2.Contains(rectangle1))
{
return rectangle1;
}
else
{
int x = Math.Max(rectangle1.Left, rectangle2.Left);
int y = Math.Max(rectangle1.Top, rectangle2.Top);
int height = Math.Min(rectangle1.Bottom, rectangle2.Bottom) - Math.Max(rectangle1.Top, rectangle2.Top);
int width = Math.Min(rectangle1.Right, rectangle2.Right) - Math.Max(rectangle1.Left, rectangle2.Left);
return new Rectangle(x, y, width, height);
}
}
else
{
return null;
}
}
and for actually drawing on the panel:
public void DrawOnPanel(IDraw sprite, SpriteBatch spriteBatch)
{
Rectangle panelRectangle = new Rectangle(
(int)_position.X,
(int)_position.Y,
_width,
_height);
Rectangle drawRectangle = new Rectangle();
drawRectangle.X = (int)sprite.Position.X;
drawRectangle.Y = (int)sprite.Position.Y;
drawRectangle.Width = sprite.Width;
drawRectangle.Height = sprite.Height;
if (panelRectangle.Contains(drawRectangle))
{
sprite.Draw(
spriteBatch,
drawRectangle,
null);
}
else if (Intersection(panelRectangle, drawRectangle) == null)
{
return;
}
else if (Intersection(panelRectangle, drawRectangle).HasValue)
{
Rectangle intersection = Intersection(panelRectangle, drawRectangle).Value;
if (Intersection(panelRectangle, drawRectangle) == drawRectangle)
{
sprite.Draw(spriteBatch, intersection, intersection);
}
else
{
sprite.Draw(
spriteBatch,
intersection,
new Rectangle(
intersection.X - drawRectangle.X,
intersection.Y - drawRectangle.Y,
intersection.Width,
intersection.Height));
}
}
}
So I guess my question is, is there a better way to do this?
Update: Just found out about the ScissorRectangle property. This seems like a decent way to do this; it requires a RasterizerState object to be made and passed into the spritebatch.Begin overload that accepts it. Seems like this might be the best bet though. There's also the Viewport which I can apparently change around. Thoughts? :)
There are several ways to limit drawing to a portion of the screen. If the area is rectangular (which seems to be the case here), you could set the viewport (see GraphicsDevice) to the panel's surface.
For non-rectangular areas, you can use the stencil buffer or use some tricks with the depth buffer. Draw the shape of the surface in the stencil buffer or the depth buffer, set your render state to draw only pixels located in the shape you just rendered in the stencil/depth buffer, finally render your sprites.
One way of doing this is simple per-pixel collision. Although this is a bad idea if the sprites are large or numerous, this can be a very easy and fast way to get the job done with small sprites. First, do a bounding circle or bounding square collision check against the panel to see if you even need to do per-pixel detection.
Then, create a contains method that checks if the position, scale, and rotation of the sprite put it so far inside the panel that it must be totally enclosed by the panel, so you don't need per-pixel collision in that case. This can be done pretty easily by just creating a bounding square that has the width and height of the length of the sprite's diagonal, and checking for collision with that.
Finally, if both of these fail, we must do per-pixel collision. Go through and check against every pixel in the sprite to see if it is within the bounds of the panel. If it isn't set the alpha value of the pixel to 0.
Thats it.

XNA isometric tiles rendering issue

I'm currently working on a XNA game prototype. I'm trying to achieve a isometric view of the game world (or is it othographic?? I'm not sure which is the right term for this projection - see pictures).
The world should a tile-based world made of cubic tiles (e.g. similar to Minecraft's world), and I'm trying to render it in 2D by using sprites.
So I have a sprite sheet with the top face of the cube, the front face and the side (visible side) face. I draw the tiles using 3 separate calls to drawSprite, one for the top, one for the side, one for the front, using a source rectangle to pick the face I want to draw and a destination rectangle to set the position on the screen according to a formula to convert from 3D world coordinates to isometric (orthographic?).
(sample sprite:
)
This works good as long as I draw the faces, but if I try to draw fine edges of each block (as per a tile grid) I can see that I get a random rendering pattern in which some lines are overwritten by the face itself and some are not.
Please note that for my world representation, X is left to right, Y is inside screen to outside screen, and Z is up to down.
In this example I'm working only with top face-edges. Here is what I get (picture):
I don't understand why some of the lines are shown and some are not.
The rendering code I use is (note in this example I'm only drawing the topmost layers in each dimension):
/// <summary>
/// Draws the world
/// </summary>
/// <param name="spriteBatch"></param>
public void draw(SpriteBatch spriteBatch)
{
Texture2D tex = null;
// DRAW TILES
for (int z = numBlocks - 1; z >= 0; z--)
{
for (int y = 0; y < numBlocks; y++)
{
for (int x = numBlocks - 1; x >=0 ; x--)
{
myTextures.TryGetValue(myBlockManager.getBlockAt(x, y, z), out tex);
if (tex != null)
{
// TOP FACE
if (z == 0)
{
drawTop(spriteBatch, x, y, z, tex);
drawTop(spriteBatch, x, y, z, outlineTexture);
}
// FRONT FACE
if(y == numBlocks -1)
drawFront(spriteBatch, x, y, z, tex);
// SIDE FACE
if(x == 0)
drawSide(spriteBatch, x, y, z, tex);
}
}
}
}
}
private void drawTop(SpriteBatch spriteBatch, int x, int y, int z, Texture2D tex)
{
int pX = OffsetX + (int)(x * TEXTURE_TOP_X_OFFRIGHT + y * TEXTURE_SIDE_X);
int pY = OffsetY + (int)(y * TEXTURE_TOP_Y + z * TEXTURE_FRONT_Y);
topDestRect.X = pX;
topDestRect.Y = pY;
spriteBatch.Draw(tex, topDestRect, TEXTURE_TOP_RECT, Color.White);
}
I tried using a different approach, creating a second 3-tiers nested for loop after the first one, so I keep the top face drawing in the first loop and the edge highlight in the second loop (I know, this is inefficient, I should also probably avoid having a method call for each tile to draw it, but I'm just trying to get it working for now).
The results are somehow better but still not working as expected, top rows are missing, see picture:
Any idea of why I'm having this problem? In the first approach it might be a sort of z-fighting, but I'm drawing sprites in a precise order so shouldn't they overwrite what's already there?
Thanks everyone
Whoa, sorry guys I'm an idiot :) I started the batch with SpriteBatch.begin(SpriteSortMode.BackToFront) but I didn't use any z-value in the draw.
I should have used SpriteSortMode.Deferred! It's now working fine. Thanks everyone!
Try tweaking the sizes of your source and destination rectangles by 1 or 2 pixels. I have a sneaking suspicion this has something to do with the way these rectangles are handled as sort of 'outlines' of the area to be rendered and a sort of off-by-one problem. This is not expert advice, just a fellow coder's intuition.
Looks like a sub pixel precision or scaling issue. Also try to ensure your texture/tile width/height is a power of 2 (32, 64, 128, etc.) as that could make the effect less bad as well. It's really hard to tell just from those pictures.
I don't know how/if you scale everything, but you should try to avoid rounding wherever possible (especially inside your drawTop() method). Every time you round some position/coordinate chances are good you might increase the error/random offsets. Try to use double (or better: float) coordinates instead of integer.

Resources