Logo Search packages:      
Sourcecode: paje.app version File versions  Download package

DrawView+Finding.m

/*
    Copyright (c) 1998, 1999, 2000, 2001, 2003, 2004 Benhur Stein
    
    This file is part of PajÚ.

    PajÚ is free software; you can redistribute it and/or modify it under
    the terms of the GNU Lesser General Public License as published by the
    Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    PajÚ 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 Lesser General Public License
    for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with PajÚ; if not, write to the Free Software Foundation, Inc.,
    59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
// DrawView+Finding
// --------------
// methods for finding entities in DrawView.

#include "DrawView.h"
#ifdef GNUSTEP
#include <math.h>
#include <values.h>
#endif

@implementation DrawView (Finding)
/*
 * Finding Entities
 * ------------------------------------------------------------------------
 */

BOOL line_hit(double px, double py,
              double x1, double y1, double x2, double y2)
// returns YES if (px,py) lies over the line from (x1,y1) to (x2,y2)
{
    double dx = px - x1;
    double dy = py - y1;
    double w = x2 - x1;
    double h = y2 - y1;

    if (fabs(w) > fabs(h)) {
        // horizontal line
        double ny;
        if (((dx * w) < 0)
            || (fabs(dx) > fabs(w)))
            return NO;
        ny = dx / w * h;
        if (fabs(ny - dy) <= 1)
            return YES;
    } else {
        // vertical line
        double nx;
        if (((dy * h) < 0)
            || (fabs(dy) > fabs(h)))
            return NO;
        nx = dy / h * w;
        if (fabs(nx - dx) <= 1)
            return YES;
    }
    return NO;
}

- (BOOL)isPoint:(NSPoint)p insideEntity:(PajeEntity *)entity
// return YES if the pixel at point "p" would be painted by drawing "entity".
{
    NSRect r;
    int hit;
#ifndef GNUSTEP
    shapefunction *path;
    STEntityTypeLayout *entityDescriptor;
#endif

    // couldn't make PSinfill work with an arrow...
    switch ([filter drawingTypeForEntity:entity]) {
        case PajeLinkDrawingType:
            r = [self rectForEntity:entity];
            hit = line_hit(p.x, p.y, NSMinX(r), NSMinY(r), NSMaxX(r), NSMaxY(r));
            break;
        case PajeVariableDrawingType:
            r = [self rectForEntity:entity];
            //hit = NSPointInRect(p, r);
            hit = p.x >= NSMinX(r) && p.x <= NSMaxX(r)
               && p.y >= NSMinY(r) && p.y <= NSMaxY(r);
            break;
        default:
            r = [self rectForEntity:entity];
#ifdef GNUSTEP
            hit = NSPointInRect(p, r);
#else
            // test insideness with x=y=0, because PostScript has a problem with
            // insideness testing in big coordinates (>~10000)
            entityDescriptor = [self descriptorForEntityType:
                                     [filter entityTypeForEntity:entity]];
            path = [[entityDescriptor shapeFunction] function];
            PSgsave();
            path(0, 0, NSWidth(r), NSHeight(r));
            PSinfill(p.x - NSMinX(r), p.y - NSMinY(r), &hit);
            PSgrestore();
#endif
    }

    return hit;
}


- (PajeEntity *)findEntityAtPoint:(NSPoint)point
// returns the entity that is drawn at "point", or nil if there isn't any.
// if there is more than one entity, returns the one that is on top (the
// last one to be drawn.
{
    id rootInstance;
    STContainerTypeLayout *rootLayout;
    
    rootInstance = [filter rootInstance];
    rootLayout = [controller rootLayout];
    
    return [self entityAtPoint:point
                    inInstance:rootInstance
            ofLayoutDescriptor:rootLayout];
}

- (PajeEntity *)entityAtPoint:(NSPoint)point
           inEntityDescriptor:(STEntityTypeLayout *)layoutDescriptor
                  inContainer:(PajeContainer *)container
{
    id findStartTime;
    id findEndTime;
    NSEnumerator *enumerator;
//    float closestDistance = MAXFLOAT;
    PajeEntity *closestEntity = nil;
    PajeEntity *entity;

    if ([layoutDescriptor drawingType] != PajeLinkDrawingType) {
    NSRect rect = [layoutDescriptor rectInContainer:container];

    if (!NSPointInRect(point, rect)) {
        return nil;
    }
    }

    float width;
    if ([layoutDescriptor drawingType] == PajeEventDrawingType) {
        width = [(STEventTypeLayout *)layoutDescriptor width];
    } else {
        width = 1;
    }
    findStartTime = XtoTIME(point.x - width);
    findEndTime   = XtoTIME(point.x + width);
    enumerator = [filter enumeratorOfEntitiesTyped:[layoutDescriptor entityType]
                                       inContainer:container
                                          fromTime:findStartTime
                                            toTime:findEndTime];
    while ((entity = [enumerator nextObject]) != nil) {
        if ([self isPoint:point insideEntity:entity]) {
            // find the entity whose center is closest to point
//            NSRect r = [self rectForEntity:entity];
//            float dx = NSMidX(r) - point.x;
//            float dy = NSMidY(r) - point.y;
//            float distance = dx * dx + dy * dy;
//            if (distance < closestDistance) {
//                closestDistance = distance;
//NSLog(@"closest:%@ -> %@", closestEntity, entity);
                closestEntity = entity;
break;
//            }
        }
    }
//    NSLog(@"search %@ in %@: %@", edesc, container, closestEntity);
    return closestEntity;
}

- (PajeEntity *)entityAtPoint:(NSPoint)point
                   inInstance:(id)instance
           ofLayoutDescriptor:(STContainerTypeLayout *)layoutDescriptor
{
    NSEnumerator *sublayoutEnum;
    STEntityTypeLayout *sublayout;
    PajeEntity *entity;

    // search instances of each subhierarchy
    // (in reverse order, to find what's been drawn on top first)
    sublayoutEnum = [[layoutDescriptor subtypes] reverseObjectEnumerator];
    while ((sublayout = [sublayoutEnum nextObject]) != nil) {
        entity = [self entityAtPoint:point
                    layoutDescriptor:sublayout
                         inContainer:instance];
        if (entity != nil) {
            break;
        }
    }
    return entity;
}

- (PajeEntity *)entityAtPoint:(NSPoint)point
             layoutDescriptor:(STEntityTypeLayout *)layoutDescriptor
                  inContainer:(PajeContainer *)container
{
    PajeEntity *entity = nil;

    if (![layoutDescriptor isPoint:point inContainer:container]) {
        return nil;
    }

    if (![layoutDescriptor isContainer]) {
        entity = [self entityAtPoint:point
                  inEntityDescriptor:layoutDescriptor
                         inContainer:container];
    } else {
        id instance;
        STContainerTypeLayout *layout;
        
        layout = (STContainerTypeLayout *)layoutDescriptor;
        instance = [layout instanceWithPoint:point];
        if (instance != nil) {
            entity = [self entityAtPoint:point
                              inInstance:instance
                      ofLayoutDescriptor:layout];
        }
    }
    return entity;
}


- (NSArray *)findEntitiesAtPoint:(NSPoint)point
// returns the entities that are drawn at "point", or nil if there isn't any.
{
    id rootInstance;
    STContainerTypeLayout *rootLayout;
    
    rootInstance = [filter rootInstance];
    rootLayout = [controller rootLayout];
    
    return [self entitiesAtPoint:point
                      inInstance:rootInstance
              ofLayoutDescriptor:rootLayout];
}

- (NSArray *)entitiesAtPoint:(NSPoint)point
          inEntityDescriptor:(STEntityTypeLayout *)layoutDescriptor
                 inContainer:(PajeContainer *)container
{
    NSDate *findStartTime;
    NSDate *findEndTime;
    NSEnumerator *enumerator;
    PajeEntity *entity;
    NSMutableArray *array;

    if ([layoutDescriptor drawingType] != PajeLinkDrawingType) {
    NSRect rect = [layoutDescriptor rectInContainer:container];

    if (!NSPointInRect(point, rect)) {
        return nil;
    }
    }

    array = [NSMutableArray array];
    
    float width;
    if ([layoutDescriptor drawingType] == PajeEventDrawingType) {
        width = [(STEventTypeLayout *)layoutDescriptor width];
    } else {
        width = 0;
    }
    findStartTime = XtoTIME(point.x - width);
    findEndTime   = XtoTIME(point.x + width);
    enumerator = [filter enumeratorOfEntitiesTyped:[layoutDescriptor entityType]
                                       inContainer:container
                                          fromTime:findStartTime
                                            toTime:findEndTime];
    while ((entity = [enumerator nextObject]) != nil) {
        if ([self isPoint:point insideEntity:entity]) {
            [array addObject:entity];
        }
    }
    return array;
}

- (NSArray *)entitiesAtPoint:(NSPoint)point
                  inInstance:(id)instance
          ofLayoutDescriptor:(STContainerTypeLayout *)layoutDescriptor
{
    NSEnumerator *sublayoutEnum;
    STEntityTypeLayout *sublayout;
    NSArray *entities;
    NSMutableArray *array;

    array = [NSMutableArray array];
    // search instances of each subhierarchy
    // (in reverse order, to find what's been drawn on top first)
    sublayoutEnum = [[layoutDescriptor subtypes] reverseObjectEnumerator];
    while ((sublayout = [sublayoutEnum nextObject]) != nil) {
        entities = [self entitiesAtPoint:point
                        layoutDescriptor:sublayout
                             inContainer:instance];
        if (entities != nil) {
            [array addObjectsFromArray:entities];
        }
    }
    return array;
}

- (NSArray *)entitiesAtPoint:(NSPoint)point
            layoutDescriptor:(STEntityTypeLayout *)layoutDescriptor
                 inContainer:(PajeContainer *)container
{
    NSArray *entities = nil;

    if (![layoutDescriptor isPoint:point inContainer:container]) {
        return nil;
    }

    if (![layoutDescriptor isContainer]) {
        entities = [self entitiesAtPoint:point
                      inEntityDescriptor:layoutDescriptor
                             inContainer:container];
    } else {
        id instance;
        STContainerTypeLayout *layout;
        
        layout = (STContainerTypeLayout *)layoutDescriptor;
        instance = [layout instanceWithPoint:point];
        if (instance != nil) {
            entities = [self entitiesAtPoint:point
                                  inInstance:instance
                          ofLayoutDescriptor:layout];
        }
    }
    return entities;
}

- (NSRect)rectForEntity:(PajeEntity *)entity
{
    float x, x1, x2, y1, y2, min, max, scale, offset;
    NSRect rect, rect1, rect2;
    id entityType;
    int imbricationLevel;
    float newHeight;
    STEntityTypeLayout *layoutDescriptor;

    id <PajeLink> link;
    PajeContainer *sourceContainer;
    PajeContainer *destContainer;
    STContainerTypeLayout *containerDescriptor;

    if (entity == nil) return NSZeroRect;

    entityType = [filter entityTypeForEntity:entity];
    layoutDescriptor = [controller descriptorForEntityType:entityType];
    switch ([layoutDescriptor drawingType]) {
        case PajeEventDrawingType:
            rect = [layoutDescriptor rectInContainer:
                                        [filter containerForEntity:entity]];
            rect.origin.x = TIMEtoX([filter timeForEntity:entity]);
            //rect.size.height = [layoutDescriptor height];
            rect.size.width = [(STEventTypeLayout *)layoutDescriptor width];
            rect.origin.x -= rect.size.width
                      * (1 - [[layoutDescriptor shapeFunction] rightExtension]);
            //rect.origin.y -= rect.size.height
            //          * ([[layoutDescriptor shapeFunction] topExtension]);
            break;
        case PajeStateDrawingType:
            rect.origin.y = [layoutDescriptor yInContainer:[filter containerForEntity:entity]];
            rect.size.height = [layoutDescriptor height];
            x1 = TIMEtoX([filter startTimeForEntity:entity]);
            x2 = TIMEtoX([filter endTimeForEntity:entity]);
            rect.origin.x = x1;
            rect.size.width = (x2 - x1);
            imbricationLevel = [filter imbricationLevelForEntity:entity];
            newHeight = rect.size.height - [(STStateTypeLayout *)layoutDescriptor insetAmount] * imbricationLevel;
            if (newHeight < 2)
                newHeight = 2;
            if (newHeight < rect.size.height)
                rect.size.height = newHeight;
            break;
        case PajeVariableDrawingType:
            x1 = TIMEtoX([filter startTimeForEntity:entity]);
            x2 = TIMEtoX([filter endTimeForEntity:entity]);
            min = [[filter minValueForEntityType:entityType] floatValue];
            max = [[filter maxValueForEntityType:entityType] floatValue];

            if (min != max) {
                scale = -([layoutDescriptor height] - 4) / (max - min);
            } else {
                scale = 1;
            }
            offset = [layoutDescriptor yInContainer:[filter containerForEntity:entity]] + 2 - max * scale;
            y1 = [[filter valueForEntity:entity] doubleValue] * scale + offset;
            y2 = min * scale + offset;
            rect.origin.x = x1;
            rect.size.width = (x2 - x1);
            rect.origin.y = y1;
            rect.size.height = y2 - y1;
            break;
        case PajeLinkDrawingType:
            link = (id <PajeLink>)entity;
            sourceContainer = [filter sourceContainerForEntity:link];
            destContainer = [filter destContainerForEntity:link];
            containerDescriptor = (STContainerTypeLayout *)[controller descriptorForEntityType:[filter sourceEntityTypeForEntity:link]];
            rect1 = [containerDescriptor rectOfInstance:sourceContainer];
            x1 = TIMEtoX([filter startTimeForEntity:link]);
            y1 = NSMinY(rect1) + [containerDescriptor linkOffset];

            containerDescriptor = (STContainerTypeLayout *)[controller descriptorForEntityType:[filter destEntityTypeForEntity:link]];
            rect2 = [containerDescriptor rectOfInstance:destContainer];
            x2 = TIMEtoX([filter endTimeForEntity:link]);
            y2 = NSMinY(rect2) + [containerDescriptor linkOffset];

            rect.origin.x = x1;
            rect.size.width = (x2 - x1);
            rect.origin.y = y1;
            rect.size.height = (y2 - y1);
            break;
        default:
            rect = NSZeroRect;
    }

    return rect;
}
@end

Generated by  Doxygen 1.6.0   Back to index